blob: f601f7c54e27cb889a6da96d639345fa14d03ce0 [file] [log] [blame]
cerion896a1372005-01-25 12:24:25 +00001
2/*--------------------------------------------------------------------*/
3/*--- ---*/
cerion1515db92005-01-25 17:21:23 +00004/*--- This file (guest-ppc32/toIR.c) is ---*/
sewardjdbcfae72005-08-02 11:14:04 +00005/*--- Copyright (C) OpenWorks LLP. All rights reserved. ---*/
cerion896a1372005-01-25 12:24:25 +00006/*--- ---*/
7/*--------------------------------------------------------------------*/
8
9/*
10 This file is part of LibVEX, a library for dynamic binary
11 instrumentation and translation.
12
sewardj7bd6ffe2005-08-03 16:07:36 +000013 Copyright (C) 2004-2005 OpenWorks LLP. All rights reserved.
cerion896a1372005-01-25 12:24:25 +000014
sewardj7bd6ffe2005-08-03 16:07:36 +000015 This library is made available under a dual licensing scheme.
cerion896a1372005-01-25 12:24:25 +000016
sewardj7bd6ffe2005-08-03 16:07:36 +000017 If you link LibVEX against other code all of which is itself
18 licensed under the GNU General Public License, version 2 dated June
19 1991 ("GPL v2"), then you may use LibVEX under the terms of the GPL
20 v2, as appearing in the file LICENSE.GPL. If the file LICENSE.GPL
21 is missing, you can obtain a copy of the GPL v2 from the Free
22 Software Foundation Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 02110-1301, USA.
24
25 For any other uses of LibVEX, you must first obtain a commercial
26 license from OpenWorks LLP. Please contact info@open-works.co.uk
27 for information about commercial licensing.
28
29 This software is provided by OpenWorks LLP "as is" and any express
30 or implied warranties, including, but not limited to, the implied
31 warranties of merchantability and fitness for a particular purpose
32 are disclaimed. In no event shall OpenWorks LLP be liable for any
33 direct, indirect, incidental, special, exemplary, or consequential
34 damages (including, but not limited to, procurement of substitute
35 goods or services; loss of use, data, or profits; or business
36 interruption) however caused and on any theory of liability,
37 whether in contract, strict liability, or tort (including
38 negligence or otherwise) arising in any way out of the use of this
39 software, even if advised of the possibility of such damage.
cerion896a1372005-01-25 12:24:25 +000040
41 Neither the names of the U.S. Department of Energy nor the
42 University of California nor the names of its contributors may be
43 used to endorse or promote products derived from this software
44 without prior written permission.
cerion896a1372005-01-25 12:24:25 +000045*/
46
cerionedf7fc52005-11-18 20:57:41 +000047/* TODO 18/Nov/05:
sewardjb51f0f42005-07-18 11:38:02 +000048
cerionedf7fc52005-11-18 20:57:41 +000049 Spot rlwimi cases which are simply left/right shifts and
sewardje14bb9f2005-07-22 09:39:02 +000050 emit Shl32/Shr32 accordingly.
51
cerionedf7fc52005-11-18 20:57:41 +000052 Altivec
53 - datastream insns
54 - lvxl,stvxl: load/store with 'least recently used' hint
55 - vexptefp, vlogefp
sewardj87e651f2005-09-09 08:31:18 +000056
57 LIMITATIONS:
58
59 Various, including:
60
61 - Some invalid forms of lswi and lswx are accepted when they should
62 not be.
63
cerionedf7fc52005-11-18 20:57:41 +000064 - Floating Point:
65 - All exceptions disabled in FPSCR
66 - condition codes not set in FPSCR
67 - some error in accuracy
cerion76de5cf2005-11-18 18:25:12 +000068
cerionedf7fc52005-11-18 20:57:41 +000069 - Altivec floating point:
70 - vmaddfp, vnmsubfp
71 Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
72 system default of Non-Java mode, we get some small errors
73 (lowest bit only).
74 This is because Non-Java mode brutally hacks denormalised results
75 to zero, whereas we keep maximum accuracy. However, using
76 Non-Java mode would give us more inaccuracy, as our intermediate
77 results would then be zeroed, too.
sewardjb51f0f42005-07-18 11:38:02 +000078*/
79
80
cerion1515db92005-01-25 17:21:23 +000081/* Translates PPC32 code to IR. */
cerion896a1372005-01-25 12:24:25 +000082
cerion645c9302005-01-31 10:09:59 +000083/* References
ceriona982c052005-06-28 17:23:09 +000084
85#define PPC32
cerion645c9302005-01-31 10:09:59 +000086 "PowerPC Microprocessor Family:
cerione9d361a2005-03-04 17:35:29 +000087 The Programming Environments for 32-Bit Microprocessors"
88 02/21/2000
89 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
90
ceriona982c052005-06-28 17:23:09 +000091#define AV
92 "PowerPC Microprocessor Family:
93 AltiVec(TM) Technology Programming Environments Manual"
94 07/10/2003
95 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
96
cerione9d361a2005-03-04 17:35:29 +000097 Other refs:
98 "PowerPC Microprocessor Family:
cerion645c9302005-01-31 10:09:59 +000099 Programming Environments Manual for 64 and 32-Bit Microprocessors
100 Version 2.0"
cerion26d07b22005-02-02 17:13:28 +0000101 06/10/2003
cerion645c9302005-01-31 10:09:59 +0000102 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F6153E213FDD912E87256D49006C6541
103*/
104
cerion896a1372005-01-25 12:24:25 +0000105#include "libvex_basictypes.h"
106#include "libvex_ir.h"
107#include "libvex.h"
cerion1515db92005-01-25 17:21:23 +0000108#include "libvex_guest_ppc32.h"
cerion896a1372005-01-25 12:24:25 +0000109
110#include "main/vex_util.h"
111#include "main/vex_globals.h"
sewardj9e6491a2005-07-02 19:24:10 +0000112#include "guest-generic/bb_to_IR.h"
cerion1515db92005-01-25 17:21:23 +0000113#include "guest-ppc32/gdefs.h"
cerion896a1372005-01-25 12:24:25 +0000114
115
116/*------------------------------------------------------------*/
117/*--- Globals ---*/
118/*------------------------------------------------------------*/
119
sewardj9e6491a2005-07-02 19:24:10 +0000120/* These are set at the start of the translation of an insn, right
121 down in disInstr_PPC32, so that we don't have to pass them around
122 endlessly. They are all constant during the translation of any
123 given insn. */
cerion896a1372005-01-25 12:24:25 +0000124
cerioned623db2005-06-20 12:42:04 +0000125/* We need to know this to do sub-register accesses correctly. */
cerioned623db2005-06-20 12:42:04 +0000126static Bool host_is_bigendian;
127
cerion896a1372005-01-25 12:24:25 +0000128/* Pointer to the guest code area. */
cerion896a1372005-01-25 12:24:25 +0000129static UChar* guest_code;
130
131/* The guest address corresponding to guest_code[0]. */
sewardj9e6491a2005-07-02 19:24:10 +0000132static Addr32 guest_CIA_bbstart;
cerion896a1372005-01-25 12:24:25 +0000133
sewardj01a9e802005-02-01 20:46:00 +0000134/* The guest address for the instruction currently being
135 translated. */
sewardj9e6491a2005-07-02 19:24:10 +0000136static Addr32 guest_CIA_curr_instr;
sewardj01a9e802005-02-01 20:46:00 +0000137
cerion896a1372005-01-25 12:24:25 +0000138/* The IRBB* into which we're generating code. */
139static IRBB* irbb;
140
141
142/*------------------------------------------------------------*/
143/*--- Debugging output ---*/
144/*------------------------------------------------------------*/
145
cerione9d361a2005-03-04 17:35:29 +0000146#define PPC32_TOIR_DEBUG 0
147
cerion896a1372005-01-25 12:24:25 +0000148#define DIP(format, args...) \
149 if (vex_traceflags & VEX_TRACE_FE) \
150 vex_printf(format, ## args)
151
152#define DIS(buf, format, args...) \
153 if (vex_traceflags & VEX_TRACE_FE) \
154 vex_sprintf(buf, format, ## args)
155
cerion76de5cf2005-11-18 18:25:12 +0000156#if PPC32_TOIR_DEBUG
157static void vex_printf_binary( UInt x, UInt len, Bool spaces )
158{
159 UInt i;
160 vassert(len > 0 && len <= 32);
161
162 for (i=len; i>0; i--) {
163 vex_printf("%d", ((x & (1<<(len-1))) != 0) );
164 x = x << 1;
165 if (((i-1)%4)==0 && (i > 1) && spaces) {
166 vex_printf(" ");
167 }
168 }
169}
170#endif
171
cerion896a1372005-01-25 12:24:25 +0000172
cerion896a1372005-01-25 12:24:25 +0000173/*------------------------------------------------------------*/
cerion38674602005-02-08 02:19:25 +0000174/*--- Offsets of various parts of the ppc32 guest state. ---*/
cerion896a1372005-01-25 12:24:25 +0000175/*------------------------------------------------------------*/
176
cerion91ad5362005-01-27 23:02:41 +0000177#define OFFB_CIA offsetof(VexGuestPPC32State,guest_CIA)
178#define OFFB_LR offsetof(VexGuestPPC32State,guest_LR)
179#define OFFB_CTR offsetof(VexGuestPPC32State,guest_CTR)
180
sewardjb51f0f42005-07-18 11:38:02 +0000181#define OFFB_XER_SO offsetof(VexGuestPPC32State,guest_XER_SO)
sewardj20ef5472005-07-21 14:48:31 +0000182#define OFFB_XER_OV offsetof(VexGuestPPC32State,guest_XER_OV)
sewardjb51f0f42005-07-18 11:38:02 +0000183#define OFFB_XER_CA offsetof(VexGuestPPC32State,guest_XER_CA)
sewardj20ef5472005-07-21 14:48:31 +0000184#define OFFB_XER_BC offsetof(VexGuestPPC32State,guest_XER_BC)
cerion91ad5362005-01-27 23:02:41 +0000185
cerion094d1392005-06-20 13:45:57 +0000186#define OFFB_FPROUND offsetof(VexGuestPPC32State,guest_FPROUND)
187
ceriona982c052005-06-28 17:23:09 +0000188#define OFFB_VRSAVE offsetof(VexGuestPPC32State,guest_VRSAVE)
189#define OFFB_VSCR offsetof(VexGuestPPC32State,guest_VSCR)
190
cerion094d1392005-06-20 13:45:57 +0000191#define OFFB_EMWARN offsetof(VexGuestPPC32State,guest_EMWARN)
192
sewardj7ce9d152005-03-15 16:54:13 +0000193#define OFFB_TISTART offsetof(VexGuestPPC32State,guest_TISTART)
194#define OFFB_TILEN offsetof(VexGuestPPC32State,guest_TILEN)
cerion91ad5362005-01-27 23:02:41 +0000195
sewardj7787af42005-08-04 18:32:19 +0000196#define OFFB_RESVN offsetof(VexGuestPPC32State,guest_RESVN)
197
cerion91ad5362005-01-27 23:02:41 +0000198
cerion38674602005-02-08 02:19:25 +0000199/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000200/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000201/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000202
cerion76de5cf2005-11-18 18:25:12 +0000203/* Extract field from insn, given idx (zero = lsb) and field length */
204#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
205
sewardjb51f0f42005-07-18 11:38:02 +0000206/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000207static UChar ifieldOPC( UInt instr ) {
208 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000209}
cerione9d361a2005-03-04 17:35:29 +0000210
cerion76de5cf2005-11-18 18:25:12 +0000211/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000212static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000213 return IFIELD( instr, 1, 10 );
214}
215
216/* Extract 9-bit secondary opcode, instr[9:1] */
217static UInt ifieldOPClo9 ( UInt instr) {
218 return IFIELD( instr, 1, 9 );
219}
220
221/* Extract 5-bit secondary opcode, instr[5:1] */
222static UInt ifieldOPClo5 ( UInt instr) {
223 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000224}
cerione9d361a2005-03-04 17:35:29 +0000225
sewardjb51f0f42005-07-18 11:38:02 +0000226/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000227static UChar ifieldRegDS( UInt instr ) {
228 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000229}
cerion094d1392005-06-20 13:45:57 +0000230
cerion76de5cf2005-11-18 18:25:12 +0000231/* Extract RA (1st source register) field, instr[20:16] */
232static UChar ifieldRegA ( UInt instr ) {
233 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000234}
235
cerion76de5cf2005-11-18 18:25:12 +0000236/* Extract RB (2nd source register) field, instr[15:11] */
237static UChar ifieldRegB ( UInt instr ) {
238 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000239}
240
cerion76de5cf2005-11-18 18:25:12 +0000241/* Extract RC (3rd source register) field, instr[10:6] */
242static UChar ifieldRegC ( UInt instr ) {
243 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000244}
245
cerion76de5cf2005-11-18 18:25:12 +0000246/* Extract 2nd lowest bit, instr[1] */
247static UChar ifieldBIT10 ( UInt instr ) {
248 return toUChar( IFIELD( instr, 10, 1 ) );
249}
250
251/* Extract 2nd lowest bit, instr[1] */
252static UChar ifieldBIT1 ( UInt instr ) {
253 return toUChar( IFIELD( instr, 1, 1 ) );
254}
255
256/* Extract lowest bit, instr[0] */
257static UChar ifieldBIT0 ( UInt instr ) {
258 return toUChar( instr & 0x1 );
259}
260
261/* Extract unsigned bottom half, instr[15:0] */
262static UInt ifieldUIMM16 ( UInt instr ) {
263 return instr & 0xFFFF;
264}
265
266/* Extract bottom half, instr[15:0], and sign extent to 32 bits */
sewardjb51f0f42005-07-18 11:38:02 +0000267static Int ifieldSIMM16 ( UInt instr ) {
268 Int i = instr & 0xFFFF;
269 return (i << 16) >> 16;
270}
271
cerion76de5cf2005-11-18 18:25:12 +0000272/* Extract bottom 26 bits, instr[25:0], and sign extent to 32 bits */
273static Int ifieldSIMM26 ( UInt instr ) {
274 Int i = instr & 0x3FFFFFF;
275 return (i << 6) >> 6;
276}
277
sewardjb51f0f42005-07-18 11:38:02 +0000278
cerionedf7fc52005-11-18 20:57:41 +0000279/*------------------------------------------------------------*/
280/*--- Special purpose registers (SPRs) ---*/
281/*--- All non-GPR/FPRs, not only strict SPR's {XER,LR,CTR} ---*/
282/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000283
cerione9d361a2005-03-04 17:35:29 +0000284typedef enum {
285 PPC32_SPR_CIA, // Current Instruction Address
286 PPC32_SPR_LR, // Link Register
287 PPC32_SPR_CTR, // Count Register
cerionedf7fc52005-11-18 20:57:41 +0000288 PPC32_SPR_XER, // Overflow, carry flags, byte count
289 PPC32_SPR_CR, // Condition Register
sewardje14bb9f2005-07-22 09:39:02 +0000290 PPC32_SPR_FPSCR, // Floating Point Status/Control Register
ceriona982c052005-06-28 17:23:09 +0000291 PPC32_SPR_VRSAVE, // Vector Save/Restore Register
cerion225a0342005-09-12 20:49:09 +0000292 PPC32_SPR_VSCR, // Vector Status and Control Register
sewardje14bb9f2005-07-22 09:39:02 +0000293 PPC32_SPR_MAX
cerione9d361a2005-03-04 17:35:29 +0000294} PPC32SPR;
295
cerionedf7fc52005-11-18 20:57:41 +0000296#define MASK_FPSCR_RN 0x3
297#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000298
cerionedf7fc52005-11-18 20:57:41 +0000299
300/*------------------------------------------------------------*/
301/*--- FP Helpers ---*/
302/*------------------------------------------------------------*/
303
sewardje14bb9f2005-07-22 09:39:02 +0000304static void put_emwarn ( IRExpr* e /* :: Ity_I32 */ );
cerion094d1392005-06-20 13:45:57 +0000305
sewardj2ead5222005-11-23 03:53:45 +0000306/* Produce the 32-bit pattern corresponding to the supplied
307 float. */
308static UInt float_to_bits ( Float f )
309{
310 union { UInt i; Float f; } u;
311 vassert(4 == sizeof(UInt));
312 vassert(4 == sizeof(Float));
313 vassert(4 == sizeof(u));
314 u.f = f;
315 return u.i;
316}
317
cerion38674602005-02-08 02:19:25 +0000318
cerion38674602005-02-08 02:19:25 +0000319/*------------------------------------------------------------*/
320/*--- Misc Helpers ---*/
321/*------------------------------------------------------------*/
322
323static UInt MASK( UInt begin, UInt end )
324{
cerionb85e8bb2005-02-16 08:54:33 +0000325 UInt m1 = ((UInt)(-1)) << begin;
326 UInt m2 = ((UInt)(-1)) << (end + 1);
327 UInt mask = m1 ^ m2;
328 if (begin > end) mask = ~mask; // wrap mask
329 return mask;
cerion38674602005-02-08 02:19:25 +0000330}
331
cerion896a1372005-01-25 12:24:25 +0000332
cerion896a1372005-01-25 12:24:25 +0000333/*------------------------------------------------------------*/
334/*--- Helper bits and pieces for deconstructing the ---*/
sewardj684aa952005-01-30 12:52:14 +0000335/*--- ppc32 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000336/*------------------------------------------------------------*/
337
338/* Add a statement to the list held by "irbb". */
339static void stmt ( IRStmt* st )
340{
341 addStmtToIRBB( irbb, st );
342}
343
cerion896a1372005-01-25 12:24:25 +0000344/* Generate a new temporary of the given type. */
345static IRTemp newTemp ( IRType ty )
346{
sewardj496a58d2005-03-20 18:44:44 +0000347 vassert(isPlausibleIRType(ty));
cerion896a1372005-01-25 12:24:25 +0000348 return newIRTemp( irbb->tyenv, ty );
349}
cerion896a1372005-01-25 12:24:25 +0000350
cerion32aad402005-09-10 12:02:24 +0000351/* Various simple conversions */
352
353static UChar extend_s_5to8 ( UChar x )
354{
355 return toUChar((((Int)x) << 27) >> 27);
356}
357
cerion92d9d872005-09-15 21:58:50 +0000358static UInt extend_s_8to32( UChar x )
359{
360 return (UInt)((((Int)x) << 24) >> 24);
361}
cerion91ad5362005-01-27 23:02:41 +0000362
cerion896a1372005-01-25 12:24:25 +0000363static UInt extend_s_16to32 ( UInt x )
364{
365 return (UInt)((((Int)x) << 16) >> 16);
366}
cerion896a1372005-01-25 12:24:25 +0000367
sewardj684aa952005-01-30 12:52:14 +0000368/* Do a big-endian load of a 32-bit word, regardless of the endianness
369 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000370static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000371{
cerioncf004462005-01-31 15:24:55 +0000372 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000373 w = (w << 8) | p[0];
374 w = (w << 8) | p[1];
375 w = (w << 8) | p[2];
376 w = (w << 8) | p[3];
377 return w;
378}
379
cerion896a1372005-01-25 12:24:25 +0000380
381/*------------------------------------------------------------*/
382/*--- Helpers for constructing IR. ---*/
383/*------------------------------------------------------------*/
384
cerion896a1372005-01-25 12:24:25 +0000385static void assign ( IRTemp dst, IRExpr* e )
386{
387 stmt( IRStmt_Tmp(dst, e) );
388}
389
cerionae694622005-01-28 17:52:47 +0000390static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000391{
sewardj7787af42005-08-04 18:32:19 +0000392 vassert(typeOfIRExpr(irbb->tyenv, addr) == Ity_I32);
sewardjaf1ceca2005-06-30 23:31:27 +0000393 stmt( IRStmt_Store(Iend_BE,addr,data) );
cerion896a1372005-01-25 12:24:25 +0000394}
395
396static IRExpr* unop ( IROp op, IRExpr* a )
397{
398 return IRExpr_Unop(op, a);
399}
400
401static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
402{
403 return IRExpr_Binop(op, a1, a2);
404}
405
406static IRExpr* mkexpr ( IRTemp tmp )
407{
408 return IRExpr_Tmp(tmp);
409}
410
sewardj7f080782005-07-27 00:22:15 +0000411//uu static IRExpr* mkU1 ( UInt i )
412//uu {
413//uu vassert(i < 2);
414//uu return IRExpr_Const(IRConst_U1( toBool(i) ));
415//uu }
cerion45552a92005-02-03 18:20:22 +0000416
sewardj684c0372005-02-07 02:33:58 +0000417static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000418{
cerion896a1372005-01-25 12:24:25 +0000419 return IRExpr_Const(IRConst_U8(i));
420}
cerion896a1372005-01-25 12:24:25 +0000421
cerion92d9d872005-09-15 21:58:50 +0000422static IRExpr* mkU16 ( UInt i )
423{
424 return IRExpr_Const(IRConst_U16(i));
425}
426
cerion896a1372005-01-25 12:24:25 +0000427static IRExpr* mkU32 ( UInt i )
428{
429 return IRExpr_Const(IRConst_U32(i));
430}
431
cerion4a49b032005-11-08 16:23:07 +0000432static IRExpr* mkU64 ( ULong i )
433{
434 return IRExpr_Const(IRConst_U64(i));
435}
436
cerionae694622005-01-28 17:52:47 +0000437static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000438{
sewardjaf1ceca2005-06-30 23:31:27 +0000439 return IRExpr_Load(Iend_BE,ty,data);
cerion896a1372005-01-25 12:24:25 +0000440}
cerion896a1372005-01-25 12:24:25 +0000441
sewardj20ef5472005-07-21 14:48:31 +0000442static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
443{
444 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
445 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
446 return
447 unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
448 unop(Iop_1Uto32, arg2)));
449}
450
451static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
452{
453 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
454 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
455 return
456 unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
457 unop(Iop_1Uto32, arg2)));
458}
sewardjb51f0f42005-07-18 11:38:02 +0000459
cerion4a49b032005-11-08 16:23:07 +0000460/* expand V128_8Ux16 to 2x V128_16Ux8's */
461static void expand8Ux16( IRExpr* vIn, /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
462{
463 IRTemp ones8x16 = newTemp(Ity_V128);
464
465 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
466 vassert(vEvn && *vEvn == IRTemp_INVALID);
467 vassert(vOdd && *vOdd == IRTemp_INVALID);
468 *vEvn = newTemp(Ity_V128);
469 *vOdd = newTemp(Ity_V128);
470
471 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000472 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
473 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
474 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000475}
476
477/* expand V128_8Sx16 to 2x V128_16Sx8's */
478static void expand8Sx16( IRExpr* vIn, /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
479{
480 IRTemp ones8x16 = newTemp(Ity_V128);
481
482 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
483 vassert(vEvn && *vEvn == IRTemp_INVALID);
484 vassert(vOdd && *vOdd == IRTemp_INVALID);
485 *vEvn = newTemp(Ity_V128);
486 *vOdd = newTemp(Ity_V128);
487
488 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000489 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
490 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
491 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000492}
493
494/* expand V128_16Uto8 to 2x V128_32Ux4's */
495static void expand16Ux8( IRExpr* vIn, /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
496{
497 IRTemp ones16x8 = newTemp(Ity_V128);
498
499 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
500 vassert(vEvn && *vEvn == IRTemp_INVALID);
501 vassert(vOdd && *vOdd == IRTemp_INVALID);
502 *vEvn = newTemp(Ity_V128);
503 *vOdd = newTemp(Ity_V128);
504
505 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000506 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
507 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
508 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000509}
510
511/* expand V128_16Sto8 to 2x V128_32Sx4's */
512static void expand16Sx8( IRExpr* vIn, /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
513{
514 IRTemp ones16x8 = newTemp(Ity_V128);
515
516 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
517 vassert(vEvn && *vEvn == IRTemp_INVALID);
518 vassert(vOdd && *vOdd == IRTemp_INVALID);
519 *vEvn = newTemp(Ity_V128);
520 *vOdd = newTemp(Ity_V128);
521
522 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000523 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
524 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
525 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000526}
527
528/* break V128 to 4xI32's, then sign-extend to I64's */
529static void breakV128to4x64S( IRExpr* t128,
530 /*OUTs*/
531 IRTemp* t3, IRTemp* t2,
532 IRTemp* t1, IRTemp* t0 )
533{
534 IRTemp hi64 = newTemp(Ity_I64);
535 IRTemp lo64 = newTemp(Ity_I64);
536
537 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
538 vassert(t0 && *t0 == IRTemp_INVALID);
539 vassert(t1 && *t1 == IRTemp_INVALID);
540 vassert(t2 && *t2 == IRTemp_INVALID);
541 vassert(t3 && *t3 == IRTemp_INVALID);
542 *t0 = newTemp(Ity_I64);
543 *t1 = newTemp(Ity_I64);
544 *t2 = newTemp(Ity_I64);
545 *t3 = newTemp(Ity_I64);
546
547 assign( hi64, unop(Iop_V128HIto64, t128) );
548 assign( lo64, unop(Iop_V128to64, t128) );
549 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
550 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
551 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
552 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
553}
554
555/* break V128 to 4xI32's, then zero-extend to I64's */
556static void breakV128to4x64U ( IRExpr* t128,
557 /*OUTs*/
558 IRTemp* t3, IRTemp* t2,
559 IRTemp* t1, IRTemp* t0 )
560{
561 IRTemp hi64 = newTemp(Ity_I64);
562 IRTemp lo64 = newTemp(Ity_I64);
563
564 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
565 vassert(t0 && *t0 == IRTemp_INVALID);
566 vassert(t1 && *t1 == IRTemp_INVALID);
567 vassert(t2 && *t2 == IRTemp_INVALID);
568 vassert(t3 && *t3 == IRTemp_INVALID);
569 *t0 = newTemp(Ity_I64);
570 *t1 = newTemp(Ity_I64);
571 *t2 = newTemp(Ity_I64);
572 *t3 = newTemp(Ity_I64);
573
574 assign( hi64, unop(Iop_V128HIto64, t128) );
575 assign( lo64, unop(Iop_V128to64, t128) );
576 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
577 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
578 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
579 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
580}
581
582/* Signed saturating narrow 64S to 32 */
583static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
584{
585 IRTemp hi32 = newTemp(Ity_I32);
586 IRTemp lo32 = newTemp(Ity_I32);
587
588 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
589
590 assign( hi32, unop(Iop_64HIto32, t64));
591 assign( lo32, unop(Iop_64to32, t64));
592
593 return IRExpr_Mux0X(
594 /* if (hi32 == (lo32 >>s 31)) */
595 unop(Iop_1Uto8,
596 binop(Iop_CmpEQ32, mkexpr(hi32),
597 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
598 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
599 binop(Iop_Add32, mkU32(0x7FFFFFFF),
600 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
601 /* then: within signed-32 range: lo half good enough */
602 mkexpr(lo32) );
603}
604
605/* Unsigned saturating narrow 64S to 32 */
606static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
607{
608 IRTemp hi32 = newTemp(Ity_I32);
609 IRTemp lo32 = newTemp(Ity_I32);
610
611 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
612
613 assign( hi32, unop(Iop_64HIto32, t64));
614 assign( lo32, unop(Iop_64to32, t64));
615
616 return IRExpr_Mux0X(
617 /* if (top 32 bits of t64 are 0) */
618 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
619 /* else: positive saturate -> 0xFFFFFFFF */
620 mkU32(0xFFFFFFFF),
621 /* then: within unsigned-32 range: lo half good enough */
622 mkexpr(lo32) );
623}
624
625/* Signed saturate narrow 64->32, combining to V128 */
626static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
627 IRExpr* t1, IRExpr* t0 )
628{
629 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
630 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
631 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
632 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
633 return binop(Iop_64HLtoV128,
634 binop(Iop_32HLto64,
635 mkQNarrow64Sto32( t3 ),
636 mkQNarrow64Sto32( t2 )),
637 binop(Iop_32HLto64,
638 mkQNarrow64Sto32( t1 ),
639 mkQNarrow64Sto32( t0 )));
640}
641
642/* Unsigned saturate narrow 64->32, combining to V128 */
643static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
644 IRExpr* t1, IRExpr* t0 )
645{
646 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
647 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
648 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
649 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
650 return binop(Iop_64HLtoV128,
651 binop(Iop_32HLto64,
652 mkQNarrow64Uto32( t3 ),
653 mkQNarrow64Uto32( t2 )),
654 binop(Iop_32HLto64,
655 mkQNarrow64Uto32( t1 ),
656 mkQNarrow64Uto32( t0 )));
657}
658
cerion24d06f12005-11-09 21:34:20 +0000659/* Simulate irops Iop_MullOdd*, since we don't have them */
660#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
661 binop(Iop_MullEven8Ux16, \
662 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
663 binop(Iop_ShrV128, expr_vB, mkU8(8)))
664
665#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
666 binop(Iop_MullEven8Sx16, \
667 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
668 binop(Iop_ShrV128, expr_vB, mkU8(8)))
669
670#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
671 binop(Iop_MullEven16Ux8, \
672 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
673 binop(Iop_ShrV128, expr_vB, mkU8(16)))
674
675#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
676 binop(Iop_MullEven16Sx8, \
677 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
678 binop(Iop_ShrV128, expr_vB, mkU8(16)))
679
680
cerion4a49b032005-11-08 16:23:07 +0000681
sewardjb51f0f42005-07-18 11:38:02 +0000682static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000683{
sewardjb51f0f42005-07-18 11:38:02 +0000684 vassert(archreg < 32);
685
686 // jrs: probably not necessary; only matters if we reference sub-parts
687 // of the ppc32 registers, but that isn't the case
688 // later: this might affect Altivec though?
689 vassert(host_is_bigendian);
690
691 switch (archreg) {
692 case 0: return offsetof(VexGuestPPC32State, guest_GPR0);
693 case 1: return offsetof(VexGuestPPC32State, guest_GPR1);
694 case 2: return offsetof(VexGuestPPC32State, guest_GPR2);
695 case 3: return offsetof(VexGuestPPC32State, guest_GPR3);
696 case 4: return offsetof(VexGuestPPC32State, guest_GPR4);
697 case 5: return offsetof(VexGuestPPC32State, guest_GPR5);
698 case 6: return offsetof(VexGuestPPC32State, guest_GPR6);
699 case 7: return offsetof(VexGuestPPC32State, guest_GPR7);
700 case 8: return offsetof(VexGuestPPC32State, guest_GPR8);
701 case 9: return offsetof(VexGuestPPC32State, guest_GPR9);
702 case 10: return offsetof(VexGuestPPC32State, guest_GPR10);
703 case 11: return offsetof(VexGuestPPC32State, guest_GPR11);
704 case 12: return offsetof(VexGuestPPC32State, guest_GPR12);
705 case 13: return offsetof(VexGuestPPC32State, guest_GPR13);
706 case 14: return offsetof(VexGuestPPC32State, guest_GPR14);
707 case 15: return offsetof(VexGuestPPC32State, guest_GPR15);
708 case 16: return offsetof(VexGuestPPC32State, guest_GPR16);
709 case 17: return offsetof(VexGuestPPC32State, guest_GPR17);
710 case 18: return offsetof(VexGuestPPC32State, guest_GPR18);
711 case 19: return offsetof(VexGuestPPC32State, guest_GPR19);
712 case 20: return offsetof(VexGuestPPC32State, guest_GPR20);
713 case 21: return offsetof(VexGuestPPC32State, guest_GPR21);
714 case 22: return offsetof(VexGuestPPC32State, guest_GPR22);
715 case 23: return offsetof(VexGuestPPC32State, guest_GPR23);
716 case 24: return offsetof(VexGuestPPC32State, guest_GPR24);
717 case 25: return offsetof(VexGuestPPC32State, guest_GPR25);
718 case 26: return offsetof(VexGuestPPC32State, guest_GPR26);
719 case 27: return offsetof(VexGuestPPC32State, guest_GPR27);
720 case 28: return offsetof(VexGuestPPC32State, guest_GPR28);
721 case 29: return offsetof(VexGuestPPC32State, guest_GPR29);
722 case 30: return offsetof(VexGuestPPC32State, guest_GPR30);
723 case 31: return offsetof(VexGuestPPC32State, guest_GPR31);
724 default: break;
725 }
726 vpanic("integerGuestRegOffset(ppc32,be)"); /*notreached*/
727}
728
729static IRExpr* getIReg ( UInt archreg )
730{
731 vassert(archreg < 32);
732 return IRExpr_Get( integerGuestRegOffset(archreg), Ity_I32 );
733}
734
735/* Ditto, but write to a reg instead. */
736static void putIReg ( UInt archreg, IRExpr* e )
737{
738 vassert(archreg < 32);
739 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I32);
740 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
741}
742
743
744static Int floatGuestRegOffset ( UInt archreg )
745{
746 vassert(archreg < 32);
747
748 switch (archreg) {
749 case 0: return offsetof(VexGuestPPC32State, guest_FPR0);
750 case 1: return offsetof(VexGuestPPC32State, guest_FPR1);
751 case 2: return offsetof(VexGuestPPC32State, guest_FPR2);
752 case 3: return offsetof(VexGuestPPC32State, guest_FPR3);
753 case 4: return offsetof(VexGuestPPC32State, guest_FPR4);
754 case 5: return offsetof(VexGuestPPC32State, guest_FPR5);
755 case 6: return offsetof(VexGuestPPC32State, guest_FPR6);
756 case 7: return offsetof(VexGuestPPC32State, guest_FPR7);
757 case 8: return offsetof(VexGuestPPC32State, guest_FPR8);
758 case 9: return offsetof(VexGuestPPC32State, guest_FPR9);
759 case 10: return offsetof(VexGuestPPC32State, guest_FPR10);
760 case 11: return offsetof(VexGuestPPC32State, guest_FPR11);
761 case 12: return offsetof(VexGuestPPC32State, guest_FPR12);
762 case 13: return offsetof(VexGuestPPC32State, guest_FPR13);
763 case 14: return offsetof(VexGuestPPC32State, guest_FPR14);
764 case 15: return offsetof(VexGuestPPC32State, guest_FPR15);
765 case 16: return offsetof(VexGuestPPC32State, guest_FPR16);
766 case 17: return offsetof(VexGuestPPC32State, guest_FPR17);
767 case 18: return offsetof(VexGuestPPC32State, guest_FPR18);
768 case 19: return offsetof(VexGuestPPC32State, guest_FPR19);
769 case 20: return offsetof(VexGuestPPC32State, guest_FPR20);
770 case 21: return offsetof(VexGuestPPC32State, guest_FPR21);
771 case 22: return offsetof(VexGuestPPC32State, guest_FPR22);
772 case 23: return offsetof(VexGuestPPC32State, guest_FPR23);
773 case 24: return offsetof(VexGuestPPC32State, guest_FPR24);
774 case 25: return offsetof(VexGuestPPC32State, guest_FPR25);
775 case 26: return offsetof(VexGuestPPC32State, guest_FPR26);
776 case 27: return offsetof(VexGuestPPC32State, guest_FPR27);
777 case 28: return offsetof(VexGuestPPC32State, guest_FPR28);
778 case 29: return offsetof(VexGuestPPC32State, guest_FPR29);
779 case 30: return offsetof(VexGuestPPC32State, guest_FPR30);
780 case 31: return offsetof(VexGuestPPC32State, guest_FPR31);
781 default: break;
782 }
783 vpanic("floatGuestRegOffset(ppc32)"); /*notreached*/
784}
785
786static IRExpr* getFReg ( UInt archreg )
787{
788 vassert(archreg < 32);
789 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
790}
791
792/* Ditto, but write to a reg instead. */
793static void putFReg ( UInt archreg, IRExpr* e )
794{
795 vassert(archreg < 32);
796 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_F64);
797 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
798}
799
800
801static Int vectorGuestRegOffset ( UInt archreg )
802{
803 vassert(archreg < 32);
804
805 switch (archreg) {
806 case 0: return offsetof(VexGuestPPC32State, guest_VR0);
807 case 1: return offsetof(VexGuestPPC32State, guest_VR1);
808 case 2: return offsetof(VexGuestPPC32State, guest_VR2);
809 case 3: return offsetof(VexGuestPPC32State, guest_VR3);
810 case 4: return offsetof(VexGuestPPC32State, guest_VR4);
811 case 5: return offsetof(VexGuestPPC32State, guest_VR5);
812 case 6: return offsetof(VexGuestPPC32State, guest_VR6);
813 case 7: return offsetof(VexGuestPPC32State, guest_VR7);
814 case 8: return offsetof(VexGuestPPC32State, guest_VR8);
815 case 9: return offsetof(VexGuestPPC32State, guest_VR9);
816 case 10: return offsetof(VexGuestPPC32State, guest_VR10);
817 case 11: return offsetof(VexGuestPPC32State, guest_VR11);
818 case 12: return offsetof(VexGuestPPC32State, guest_VR12);
819 case 13: return offsetof(VexGuestPPC32State, guest_VR13);
820 case 14: return offsetof(VexGuestPPC32State, guest_VR14);
821 case 15: return offsetof(VexGuestPPC32State, guest_VR15);
822 case 16: return offsetof(VexGuestPPC32State, guest_VR16);
823 case 17: return offsetof(VexGuestPPC32State, guest_VR17);
824 case 18: return offsetof(VexGuestPPC32State, guest_VR18);
825 case 19: return offsetof(VexGuestPPC32State, guest_VR19);
826 case 20: return offsetof(VexGuestPPC32State, guest_VR20);
827 case 21: return offsetof(VexGuestPPC32State, guest_VR21);
828 case 22: return offsetof(VexGuestPPC32State, guest_VR22);
829 case 23: return offsetof(VexGuestPPC32State, guest_VR23);
830 case 24: return offsetof(VexGuestPPC32State, guest_VR24);
831 case 25: return offsetof(VexGuestPPC32State, guest_VR25);
832 case 26: return offsetof(VexGuestPPC32State, guest_VR26);
833 case 27: return offsetof(VexGuestPPC32State, guest_VR27);
834 case 28: return offsetof(VexGuestPPC32State, guest_VR28);
835 case 29: return offsetof(VexGuestPPC32State, guest_VR29);
836 case 30: return offsetof(VexGuestPPC32State, guest_VR30);
837 case 31: return offsetof(VexGuestPPC32State, guest_VR31);
838 default: break;
839 }
840 vpanic("vextorGuestRegOffset(ppc32)"); /*notreached*/
841}
842
843static IRExpr* getVReg ( UInt archreg )
844{
845 vassert(archreg < 32);
846 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
847}
848
849/* Ditto, but write to a reg instead. */
850static void putVReg ( UInt archreg, IRExpr* e )
851{
852 vassert(archreg < 32);
853 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_V128);
854 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
855}
856
857static Int guestCR321offset ( UInt cr )
858{
859 switch (cr) {
860 case 0: return offsetof(VexGuestPPC32State, guest_CR0_321 );
861 case 1: return offsetof(VexGuestPPC32State, guest_CR1_321 );
862 case 2: return offsetof(VexGuestPPC32State, guest_CR2_321 );
863 case 3: return offsetof(VexGuestPPC32State, guest_CR3_321 );
864 case 4: return offsetof(VexGuestPPC32State, guest_CR4_321 );
865 case 5: return offsetof(VexGuestPPC32State, guest_CR5_321 );
866 case 6: return offsetof(VexGuestPPC32State, guest_CR6_321 );
867 case 7: return offsetof(VexGuestPPC32State, guest_CR7_321 );
868 default: vpanic("guestCR321offset(ppc32)");
869 }
870}
871
872static Int guestCR0offset ( UInt cr )
873{
874 switch (cr) {
875 case 0: return offsetof(VexGuestPPC32State, guest_CR0_0 );
876 case 1: return offsetof(VexGuestPPC32State, guest_CR1_0 );
877 case 2: return offsetof(VexGuestPPC32State, guest_CR2_0 );
878 case 3: return offsetof(VexGuestPPC32State, guest_CR3_0 );
879 case 4: return offsetof(VexGuestPPC32State, guest_CR4_0 );
880 case 5: return offsetof(VexGuestPPC32State, guest_CR5_0 );
881 case 6: return offsetof(VexGuestPPC32State, guest_CR6_0 );
882 case 7: return offsetof(VexGuestPPC32State, guest_CR7_0 );
883 default: vpanic("guestCR3offset(ppc32)");
884 }
sewardjb51f0f42005-07-18 11:38:02 +0000885}
886
887// ROTL(src32, rot_amt5)
sewardjc9659532005-07-21 21:33:57 +0000888static IRExpr* ROTL32 ( IRExpr* src, IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +0000889{
sewardjc9659532005-07-21 21:33:57 +0000890 IRExpr* masked;
cerionb85e8bb2005-02-16 08:54:33 +0000891 vassert(typeOfIRExpr(irbb->tyenv,src) == Ity_I32);
sewardjc9659532005-07-21 21:33:57 +0000892 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I32);
sewardjb51f0f42005-07-18 11:38:02 +0000893
sewardjc9659532005-07-21 21:33:57 +0000894 masked
895 = unop(Iop_32to8, binop(Iop_And32, rot_amt, mkU32(31)));
sewardjb51f0f42005-07-18 11:38:02 +0000896
cerionb85e8bb2005-02-16 08:54:33 +0000897 // (src << rot_amt) | (src >> (32-rot_amt))
sewardjc9659532005-07-21 21:33:57 +0000898 /* Note: the MuxOX is not merely an optimisation; it's needed
899 because otherwise the Shr32 is a shift by the word size when
900 masked denotes zero. For rotates by immediates, a lot of
901 this junk gets folded out. */
902 return
903 IRExpr_Mux0X(
904 masked,
905 /* zero rotate. */
906 src,
907 /* non-zero rotate */
908 binop( Iop_Or32,
909 binop(Iop_Shl32, src, masked),
910 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), masked))
911 )
912 );
cerion45b70ff2005-01-31 17:03:25 +0000913}
cerion896a1372005-01-25 12:24:25 +0000914
sewardj87e651f2005-09-09 08:31:18 +0000915/* Do the standard effective address calculation: (rA|0) + rB. */
916static IRExpr* /* :: Ity_I32 */ ea_standard ( Int rA, Int rB )
917{
918 vassert(rA >= 0 && rA < 32);
919 vassert(rB >= 0 && rB < 32);
920 if (rA == 0) {
921 return getIReg(rB);
922 } else {
923 return binop(Iop_Add32, getIReg(rA), getIReg(rB));
924 }
925}
926
927/* Do the effective address calculation: (rA|0). */
928static IRExpr* /* :: Ity_I32 */ ea_rA_or_zero ( Int rA )
929{
930 vassert(rA >= 0 && rA < 32);
931 if (rA == 0) {
932 return mkU32(0);
933 } else {
934 return getIReg(rA);
935 }
936}
937
cerion896a1372005-01-25 12:24:25 +0000938
cerion896a1372005-01-25 12:24:25 +0000939/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000940/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +0000941/*------------------------------------------------------------*/
942
sewardjb51f0f42005-07-18 11:38:02 +0000943/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +0000944
sewardjb51f0f42005-07-18 11:38:02 +0000945 In the hardware, CR is laid out like this. The leftmost end is the
946 most significant bit in the register; however the IBM documentation
947 numbers the bits backwards for some reason.
948
949 CR0 CR1 .......... CR6 CR7
950 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
951 31 28 3 0 (normal bit numbering)
952
cerionedf7fc52005-11-18 20:57:41 +0000953 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +0000954
cerionedf7fc52005-11-18 20:57:41 +0000955 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +0000956
957 Indexing from BI to guest state:
958
959 let n = BI / 4
960 off = BI % 4
961 this references CR n:
962
cerionedf7fc52005-11-18 20:57:41 +0000963 off==0 -> guest_CRn_321 >> 3
964 off==1 -> guest_CRn_321 >> 2
965 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +0000966 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +0000967
968 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +0000969 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +0000970 3, 2 and 1 (normal notation).
971*/
cerionedf7fc52005-11-18 20:57:41 +0000972
973static void putCR321 ( UInt cr, IRExpr* e )
974{
975 vassert(cr < 8);
976 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
977 stmt( IRStmt_Put(guestCR321offset(cr), e) );
978}
979
980static void putCR0 ( UInt cr, IRExpr* e )
981{
982 vassert(cr < 8);
983 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
984 stmt( IRStmt_Put(guestCR0offset(cr), e) );
985}
986
987static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
988{
989 vassert(cr < 8);
990 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
991}
992
993static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
994{
995 vassert(cr < 8);
996 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
997}
998
sewardjb51f0f42005-07-18 11:38:02 +0000999/* Fetch the specified CR bit (as per IBM/hardware notation) and
1000 return it at the bottom of an I32; the top 31 bits are guaranteed
1001 to be zero. */
1002static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001003{
sewardjb51f0f42005-07-18 11:38:02 +00001004 UInt n = bi / 4;
1005 UInt off = bi % 4;
1006 vassert(bi < 32);
1007 if (off == 3) {
1008 /* Fetch the SO bit for this CR field */
1009 /* Note: And32 is redundant paranoia iff guest state only has 0
1010 or 1 in that slot. */
1011 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1012 } else {
1013 /* Fetch the <, > or == bit for this CR field */
1014 return binop( Iop_And32,
1015 binop( Iop_Shr32,
1016 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001017 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001018 mkU32(1) );
1019 }
cerion91ad5362005-01-27 23:02:41 +00001020}
1021
sewardjb51f0f42005-07-18 11:38:02 +00001022/* Dually, write the least significant bit of BIT to the specified CR
1023 bit. Indexing as per getCRbit. */
1024static void putCRbit ( UInt bi, IRExpr* bit )
1025{
sewardj197bd172005-10-12 11:34:33 +00001026 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001027 IRExpr* safe;
1028 vassert(typeOfIRExpr(irbb->tyenv,bit) == Ity_I32);
1029 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001030 n = bi / 4;
1031 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001032 vassert(bi < 32);
1033 if (off == 3) {
1034 /* This is the SO bit for this CR field */
1035 putCR0(n, unop(Iop_32to8, safe));
1036 } else {
1037 off = 3 - off;
1038 vassert(off == 1 || off == 2 || off == 3);
1039 putCR321(
1040 n,
1041 unop( Iop_32to8,
1042 binop( Iop_Or32,
1043 /* old value with field masked out */
1044 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1045 mkU32(~(1 << off))),
1046 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001047 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001048 )
1049 )
1050 );
1051 }
1052}
1053
sewardjb51f0f42005-07-18 11:38:02 +00001054/* Fetch the specified CR bit (as per IBM/hardware notation) and
1055 return it somewhere in an I32; it does not matter where, but
1056 whichever bit it is, all other bits are guaranteed to be zero. In
1057 other words, the I32-typed expression will be zero if the bit is
1058 zero and nonzero if the bit is 1. Write into *where the index
1059 of where the bit will be. */
1060
1061static IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
1062{
1063 UInt n = bi / 4;
1064 UInt off = bi % 4;
1065 vassert(bi < 32);
1066 if (off == 3) {
1067 /* Fetch the SO bit for this CR field */
1068 /* Note: And32 is redundant paranoia iff guest state only has 0
1069 or 1 in that slot. */
1070 *where = 0;
1071 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1072 } else {
1073 /* Fetch the <, > or == bit for this CR field */
1074 *where = 3-off;
1075 return binop( Iop_And32,
1076 unop(Iop_8Uto32, getCR321(n)),
1077 mkU32(1 << (3-off)) );
1078 }
1079}
1080
sewardjb51f0f42005-07-18 11:38:02 +00001081/* Set the CR0 flags following an arithmetic operation.
1082 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001083*/
cerionedf7fc52005-11-18 20:57:41 +00001084static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001085static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001086{
cerion62bec572005-02-01 21:29:39 +00001087 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_I32);
sewardjb51f0f42005-07-18 11:38:02 +00001088 putCR321( 0, unop(Iop_32to8,
cerionedf7fc52005-11-18 20:57:41 +00001089 binop(Iop_CmpORD32S, result, mkU32(0))) );
sewardjb51f0f42005-07-18 11:38:02 +00001090 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001091}
cerion896a1372005-01-25 12:24:25 +00001092
sewardj20ef5472005-07-21 14:48:31 +00001093
cerionedf7fc52005-11-18 20:57:41 +00001094/* Set the CR6 flags following an AltiVec compare operation. */
1095static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1096{
1097 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1098 all_ones = (v[0] && v[1] && v[2] && v[3])
1099 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1100 */
1101 IRTemp v0 = newTemp(Ity_V128);
1102 IRTemp v1 = newTemp(Ity_V128);
1103 IRTemp v2 = newTemp(Ity_V128);
1104 IRTemp v3 = newTemp(Ity_V128);
1105 IRTemp rOnes = newTemp(Ity_I8);
1106 IRTemp rZeros = newTemp(Ity_I8);
1107
1108 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_V128);
1109
1110 assign( v0, result );
1111 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1112 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1113 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1114
1115 assign( rZeros, unop(Iop_1Uto8,
1116 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1117 unop(Iop_Not32,
1118 unop(Iop_V128to32,
1119 binop(Iop_OrV128,
1120 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1121 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1122 ))) );
1123
1124 if (test_all_ones) {
1125 assign( rOnes, unop(Iop_1Uto8,
1126 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1127 unop(Iop_V128to32,
1128 binop(Iop_AndV128,
1129 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
1130 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))))) );
1131 putCR321( 6, binop(Iop_Or8,
1132 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1133 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1134 } else {
1135 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1136 }
1137 putCR0( 6, mkU8(0) );
1138}
1139
1140
1141
1142/*------------------------------------------------------------*/
1143/*--- Helpers for XER flags. ---*/
1144/*------------------------------------------------------------*/
1145
1146static void putXER_SO ( IRExpr* e )
1147{
1148 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1149 stmt( IRStmt_Put(OFFB_XER_SO, binop(Iop_And8, mkU8(1), e)) );
1150}
1151
1152static void putXER_OV ( IRExpr* e )
1153{
1154 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1155 stmt( IRStmt_Put(OFFB_XER_OV, binop(Iop_And8, mkU8(1), e)) );
1156}
1157
1158static void putXER_CA ( IRExpr* e )
1159{
1160 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1161 stmt( IRStmt_Put(OFFB_XER_CA, binop(Iop_And8, mkU8(1), e)) );
1162}
1163
1164static void putXER_BC ( IRExpr* e )
1165{
1166 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1167 stmt( IRStmt_Put(OFFB_XER_BC, binop(Iop_And8, mkU8(0x7F), e)) );
1168}
1169
1170static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1171{
1172 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
1173}
1174
1175static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1176{
1177 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1178}
1179
1180static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1181{
1182 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
1183}
1184
1185static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1186{
1187 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1188}
1189
1190static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1191{
1192 return binop( Iop_And32,
1193 unop(Iop_8Uto32, IRExpr_Get( OFFB_XER_CA, Ity_I8 )),
1194 mkU32(1) );
1195}
1196
1197static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1198{
1199 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
1200}
1201
1202static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1203{
1204 return binop( Iop_And32,
1205 unop(Iop_8Uto32, IRExpr_Get( OFFB_XER_BC, Ity_I8 )),
1206 mkU32(0x7F) );
1207}
1208
1209
sewardj20ef5472005-07-21 14:48:31 +00001210/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1211 %XER.SO accordingly. */
1212
1213static void set_XER_OV( UInt op, IRExpr* res,
1214 IRExpr* argL, IRExpr* argR )
1215{
1216 IRTemp t64;
1217 IRExpr* xer_ov;
1218 vassert(op < PPC32G_FLAG_OP_NUMBER);
1219 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1220 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1221 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1222
1223# define INT32_MIN 0x80000000
1224
1225# define XOR2(_aa,_bb) \
1226 binop(Iop_Xor32,(_aa),(_bb))
1227
1228# define XOR3(_cc,_dd,_ee) \
1229 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1230
1231# define AND3(_ff,_gg,_hh) \
1232 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1233
1234#define NOT(_jj) \
1235 unop(Iop_Not32, (_jj))
1236
1237 switch (op) {
1238
1239 case /* 0 */ PPC32G_FLAG_OP_ADD:
1240 case /* 1 */ PPC32G_FLAG_OP_ADDE:
1241 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1242 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1243 xer_ov
1244 = AND3( XOR3(argL,argR,mkU32(-1)),
1245 XOR2(argL,res),
1246 mkU32(INT32_MIN) );
cerion76de5cf2005-11-18 18:25:12 +00001247 /* xer_ov can only be 0 or 1<<31 */
sewardj20ef5472005-07-21 14:48:31 +00001248 xer_ov
1249 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1250 break;
1251
1252 case /* 2 */ PPC32G_FLAG_OP_DIVW:
1253 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1254 xer_ov
1255 = mkOR1(
1256 mkAND1(
1257 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1258 binop(Iop_CmpEQ32, argR, mkU32(-1))
1259 ),
1260 binop(Iop_CmpEQ32, argR, mkU32(0) )
1261 );
1262 xer_ov
1263 = unop(Iop_1Uto32, xer_ov);
1264 break;
1265
1266 case /* 3 */ PPC32G_FLAG_OP_DIVWU:
1267 /* argR == 0 */
1268 xer_ov
1269 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1270 break;
1271
1272 case /* 4 */ PPC32G_FLAG_OP_MULLW:
1273 /* OV true if result can't be represented in 32 bits
1274 i.e sHi != sign extension of sLo */
1275 t64 = newTemp(Ity_I64);
sewardj6fa6bf12005-07-21 17:07:18 +00001276 assign( t64, binop(Iop_MullS32, argL, argR) );
sewardj20ef5472005-07-21 14:48:31 +00001277 xer_ov
1278 = binop( Iop_CmpNE32,
1279 unop(Iop_64HIto32, mkexpr(t64)),
1280 binop( Iop_Sar32,
1281 unop(Iop_64to32, mkexpr(t64)),
1282 mkU8(31))
1283 );
1284 xer_ov
1285 = unop(Iop_1Uto32, xer_ov);
1286 break;
1287
1288 case /* 5 */ PPC32G_FLAG_OP_NEG:
1289 /* argL == INT32_MIN */
1290 xer_ov
1291 = unop( Iop_1Uto32,
1292 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1293 break;
1294
1295 case /* 6 */ PPC32G_FLAG_OP_SUBF:
1296 case /* 7 */ PPC32G_FLAG_OP_SUBFC:
1297 case /* 8 */ PPC32G_FLAG_OP_SUBFE:
1298 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1299 xer_ov
1300 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1301 XOR2(NOT(argL),res),
1302 mkU32(INT32_MIN) );
cerion76de5cf2005-11-18 18:25:12 +00001303 /* xer_ov can only be 0 or 1<<31 */
sewardj20ef5472005-07-21 14:48:31 +00001304 xer_ov
1305 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1306 break;
1307
1308 default:
sewardjc7cd2142005-09-09 22:31:49 +00001309 vex_printf("set_XER_OV: op = %u\n", op);
sewardj20ef5472005-07-21 14:48:31 +00001310 vpanic("set_XER_OV(ppc32)");
1311 }
1312
1313 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1314 stmt( IRStmt_Put( OFFB_XER_OV, unop(Iop_32to8, xer_ov) ) );
1315
1316 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001317 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001318
1319# undef INT32_MIN
1320# undef AND3
1321# undef XOR3
1322# undef XOR2
1323# undef NOT
1324}
1325
cerion38674602005-02-08 02:19:25 +00001326
sewardjb51f0f42005-07-18 11:38:02 +00001327/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1328 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001329
sewardjb51f0f42005-07-18 11:38:02 +00001330static void set_XER_CA( UInt op,
1331 IRExpr* res,
1332 IRExpr* argL,
1333 IRExpr* argR,
1334 IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001335{
sewardj9a036bf2005-03-14 18:19:08 +00001336 IRExpr* xer_ca;
cerionb85e8bb2005-02-16 08:54:33 +00001337 vassert(op < PPC32G_FLAG_OP_NUMBER);
sewardjb51f0f42005-07-18 11:38:02 +00001338 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1339 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1340 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1341 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001342
sewardj20ef5472005-07-21 14:48:31 +00001343 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1344 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001345 getXER_CA32(), which masks it accordingly. In any case it being
sewardj20ef5472005-07-21 14:48:31 +00001346 0 or 1 is an invariant of the ppc32 guest state representation;
1347 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001348
sewardj20ef5472005-07-21 14:48:31 +00001349 switch (op) {
1350
1351 case /* 0 */ PPC32G_FLAG_OP_ADD:
1352 /* res <u argL */
1353 xer_ca
1354 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1355 break;
1356
1357 case /* 1 */ PPC32G_FLAG_OP_ADDE:
1358 /* res <u argL || (old_ca==1 && res==argL) */
1359 xer_ca
1360 = mkOR1(
1361 binop(Iop_CmpLT32U, res, argL),
1362 mkAND1(
1363 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1364 binop(Iop_CmpEQ32, res, argL)
1365 )
1366 );
1367 xer_ca
1368 = unop(Iop_1Uto32, xer_ca);
1369 break;
1370
1371 case /* 8 */ PPC32G_FLAG_OP_SUBFE:
1372 /* res <u argR || (old_ca==1 && res==argR) */
1373 xer_ca
1374 = mkOR1(
1375 binop(Iop_CmpLT32U, res, argR),
1376 mkAND1(
1377 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1378 binop(Iop_CmpEQ32, res, argR)
1379 )
1380 );
1381 xer_ca
1382 = unop(Iop_1Uto32, xer_ca);
1383 break;
1384
1385 case /* 7 */ PPC32G_FLAG_OP_SUBFC:
1386 case /* 9 */ PPC32G_FLAG_OP_SUBFI:
1387 /* res <=u argR */
1388 xer_ca
1389 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1390 break;
1391
1392 case /* 10 */ PPC32G_FLAG_OP_SRAW:
1393 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1394 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1395 bit of argL. */
cerion76de5cf2005-11-18 18:25:12 +00001396 /* This term valid for shift amount < 32 only */
sewardj20ef5472005-07-21 14:48:31 +00001397 xer_ca
1398 = binop(
1399 Iop_And32,
1400 binop(Iop_Sar32, argL, mkU8(31)),
1401 binop( Iop_And32,
1402 argL,
1403 binop( Iop_Sub32,
1404 binop(Iop_Shl32, mkU32(1), unop(Iop_32to8,argR)),
1405 mkU32(1) )
1406 )
1407 );
1408 xer_ca
1409 = IRExpr_Mux0X(
1410 /* shift amt > 31 ? */
1411 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1412 /* no -- be like srawi */
1413 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1414 /* yes -- get sign bit of argL */
1415 binop(Iop_Shr32, argL, mkU8(31))
1416 );
1417 break;
1418
1419 case /* 11 */ PPC32G_FLAG_OP_SRAWI:
1420 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1421 0. Since the shift amount is known to be in the range
1422 0 .. 31 inclusive the following seems viable:
1423 xer.ca == 1 iff the following is nonzero:
1424 (argL >>s 31) -- either all 0s or all 1s
1425 & (argL & (1<<argR)-1) -- the stuff shifted out */
1426 xer_ca
1427 = binop(
1428 Iop_And32,
1429 binop(Iop_Sar32, argL, mkU8(31)),
1430 binop( Iop_And32,
1431 argL,
1432 binop( Iop_Sub32,
1433 binop(Iop_Shl32, mkU32(1), unop(Iop_32to8,argR)),
1434 mkU32(1) )
1435 )
1436 );
1437 xer_ca
1438 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1439 break;
1440
1441 default:
sewardjc7cd2142005-09-09 22:31:49 +00001442 vex_printf("set_XER_CA: op = %u\n", op);
sewardj20ef5472005-07-21 14:48:31 +00001443 vpanic("set_XER_CA(ppc32)");
1444 }
1445
1446 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001447 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001448}
1449
cerion0c439222005-09-15 14:22:58 +00001450
1451
sewardje14bb9f2005-07-22 09:39:02 +00001452/*------------------------------------------------------------*/
cerionedf7fc52005-11-18 20:57:41 +00001453/*--- SPR register interface --- */
sewardje14bb9f2005-07-22 09:39:02 +00001454/*------------------------------------------------------------*/
1455
1456/* Get a masked word from the given reg */
cerionedf7fc52005-11-18 20:57:41 +00001457static IRExpr* /* ::Ity_I32 */ getSPR_masked ( PPC32SPR reg, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00001458{
1459 IRTemp val = newTemp(Ity_I32);
1460 vassert( reg < PPC32_SPR_MAX );
1461
1462 switch (reg) {
1463
1464 case PPC32_SPR_FPSCR: {
1465 vassert((mask & 0x3) == 0x3 || (mask & 0x3) == 0x0);
1466 vassert((mask & 0xF000) == 0xF000 || (mask & 0xF000) == 0x0);
1467 /* all masks now refer to valid fields */
1468
1469 /* Vex-generated code expects to run with the FPSCR set as follows:
cerion76de5cf2005-11-18 18:25:12 +00001470 all exceptions masked, round-to-nearest.
1471 This corresponds to a FPSCR value of 0x0. */
sewardje14bb9f2005-07-22 09:39:02 +00001472
1473 /* We're only keeping track of the rounding mode,
cerion76de5cf2005-11-18 18:25:12 +00001474 so if the mask isn't asking for this, just return 0x0 */
sewardje14bb9f2005-07-22 09:39:02 +00001475 if (mask & 0x3) {
1476 assign( val, IRExpr_Get(OFFB_FPROUND, Ity_I32) );
1477 } else {
1478 assign( val, mkU32(0x0) );
1479 }
cerionedf7fc52005-11-18 20:57:41 +00001480 break;
sewardje14bb9f2005-07-22 09:39:02 +00001481 }
1482
sewardje14bb9f2005-07-22 09:39:02 +00001483 default:
cerionedf7fc52005-11-18 20:57:41 +00001484 vpanic("getSPR_masked(ppc32)");
sewardje14bb9f2005-07-22 09:39:02 +00001485 }
1486
1487 if (mask != 0xFFFFFFFF) {
1488 return binop(Iop_And32, mkexpr(val), mkU32(mask));
1489 } else {
1490 return mkexpr(val);
1491 }
1492}
1493
cerionedf7fc52005-11-18 20:57:41 +00001494/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
1495 and return it at the bottom of an I32; the top 27 bits are
1496 guaranteed to be zero. */
1497static IRExpr* /* ::Ity_I32 */ getSPR_field ( PPC32SPR reg, UInt fld )
1498{
sewardj41a7b702005-11-18 22:18:23 +00001499 UInt shft, mask;
1500
cerionedf7fc52005-11-18 20:57:41 +00001501 vassert( fld < 8 );
1502 vassert( reg < PPC32_SPR_MAX );
1503
sewardj41a7b702005-11-18 22:18:23 +00001504 shft = 4*(7-fld);
1505 mask = 0xF<<shft;
sewardje14bb9f2005-07-22 09:39:02 +00001506
cerionedf7fc52005-11-18 20:57:41 +00001507 switch (reg) {
1508 case PPC32_SPR_XER:
1509 vassert(fld ==7);
1510 return binop(Iop_Or32,
1511 binop(Iop_Or32,
1512 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
1513 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
1514 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
1515 break;
sewardje14bb9f2005-07-22 09:39:02 +00001516
cerionedf7fc52005-11-18 20:57:41 +00001517 default:
1518 if (shft == 0)
1519 return getSPR_masked( reg, mask );
1520 else
1521 return binop(Iop_Shr32,
1522 getSPR_masked( reg, mask ),
1523 mkU8(toUChar( shft )));
1524 }
1525}
1526
1527static IRExpr* /* :: Ity_I32 */ getSPR ( PPC32SPR reg )
1528{
1529 switch (reg) {
1530 case PPC32_SPR_LR:
1531 return IRExpr_Get( OFFB_LR, Ity_I32 );
1532 case PPC32_SPR_CTR:
1533 return IRExpr_Get( OFFB_CTR, Ity_I32 );
1534 case PPC32_SPR_VRSAVE:
1535 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
1536 case PPC32_SPR_VSCR:
1537 return binop(Iop_And32,
1538 IRExpr_Get( OFFB_VSCR, Ity_I32 ),
1539 mkU32(MASK_VSCR_VALID));
1540 case PPC32_SPR_CR: {
1541 /* Synthesise the entire CR into a single word. Expensive. */
1542# define FIELD(_n) \
1543 binop(Iop_Shl32, \
1544 unop(Iop_8Uto32, \
1545 binop(Iop_Or8, \
1546 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
1547 binop(Iop_And8, getCR0(_n), mkU8(1)) \
1548 ) \
1549 ), \
1550 mkU8(4 * (7-(_n))) \
1551 )
1552 return binop(Iop_Or32,
1553 binop(Iop_Or32,
1554 binop(Iop_Or32, FIELD(0), FIELD(1)),
1555 binop(Iop_Or32, FIELD(2), FIELD(3))
1556 ),
1557 binop(Iop_Or32,
1558 binop(Iop_Or32, FIELD(4), FIELD(5)),
1559 binop(Iop_Or32, FIELD(6), FIELD(7))
1560 )
1561 );
1562# undef FIELD
1563 }
1564 case PPC32_SPR_XER: {
1565 return binop(Iop_Or32,
1566 binop(Iop_Or32,
1567 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
1568 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
1569 binop(Iop_Or32,
1570 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
1571 getXER_BC32()));
1572 }
1573 default:
1574 vpanic("getSPR(ppc32)");
1575 }
1576}
sewardje14bb9f2005-07-22 09:39:02 +00001577
1578/* Write masked src to the given reg */
cerionedf7fc52005-11-18 20:57:41 +00001579static void putSPR_masked ( PPC32SPR reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00001580{
1581 vassert( reg < PPC32_SPR_MAX );
1582 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
1583
1584 switch (reg) {
cerionedf7fc52005-11-18 20:57:41 +00001585 case PPC32_SPR_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00001586 vassert((mask & 0x3) == 0x3 || (mask & 0x3) == 0x0);
1587 vassert((mask & 0xF000) == 0xF000 || (mask & 0xF000) == 0x0);
1588 /* all masks now refer to valid fields */
1589
1590 /* Allow writes to Rounding Mode */
1591 if (mask & 0x3) {
1592 stmt( IRStmt_Put( OFFB_FPROUND,
1593 binop(Iop_And32, src, mkU32(0x3)) ));
1594 }
1595
cerionedf7fc52005-11-18 20:57:41 +00001596 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00001597 - Exception Controls
1598 - Non-IEEE Mode
1599 */
1600 if (mask & 0xFC) { // Exception Control, Non-IEE mode
1601 VexEmWarn ew = EmWarn_PPC32exns;
1602
1603 /* If any of the src::exception_control bits are actually set,
1604 side-exit to the next insn, reporting the warning,
1605 so that Valgrind's dispatcher sees the warning. */
1606 put_emwarn( mkU32(ew) );
1607 stmt(
1608 IRStmt_Exit(
1609 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
1610 Ijk_EmWarn,
1611 IRConst_U32(guest_CIA_curr_instr + 4)
1612 )
1613 );
1614 }
1615
cerionedf7fc52005-11-18 20:57:41 +00001616 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00001617 break;
cerionedf7fc52005-11-18 20:57:41 +00001618 }
sewardje14bb9f2005-07-22 09:39:02 +00001619
1620 default:
cerionedf7fc52005-11-18 20:57:41 +00001621 vex_printf("putSPR_masked(ppc32): %u", reg);
1622 vpanic("putSPR_masked(ppc32)");
sewardje14bb9f2005-07-22 09:39:02 +00001623 }
1624}
1625
cerionedf7fc52005-11-18 20:57:41 +00001626/* Write the least significant nibble of src to the specified
1627 REG[FLD] (as per IBM/hardware notation). */
1628static void putSPR_field ( PPC32SPR reg, IRExpr* src, UInt fld )
1629{
sewardj41a7b702005-11-18 22:18:23 +00001630 UInt shft, mask;
1631
cerionedf7fc52005-11-18 20:57:41 +00001632 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
1633 vassert( fld < 8 );
1634 vassert( reg < PPC32_SPR_MAX );
1635
sewardj41a7b702005-11-18 22:18:23 +00001636 shft = 4*(7-fld);
1637 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00001638
1639 switch (reg) {
1640 case PPC32_SPR_CR:
1641 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
1642 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
1643 break;
1644
1645 default:
1646 if (shft == 0) {
1647 putSPR_masked( reg, src, mask );
1648 } else {
1649 putSPR_masked( reg,
1650 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
1651 mask );
1652 }
1653 }
1654}
cerion62bec572005-02-01 21:29:39 +00001655
sewardjb51f0f42005-07-18 11:38:02 +00001656static void putSPR ( PPC32SPR reg, IRExpr* src )
cerion76222262005-02-05 13:45:57 +00001657{
cerionedf7fc52005-11-18 20:57:41 +00001658 vassert( reg < PPC32_SPR_MAX );
sewardjb51f0f42005-07-18 11:38:02 +00001659 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
cerionb85e8bb2005-02-16 08:54:33 +00001660 switch (reg) {
sewardjb51f0f42005-07-18 11:38:02 +00001661 case PPC32_SPR_CIA:
1662 stmt( IRStmt_Put( OFFB_CIA, src ) );
1663 break;
1664 case PPC32_SPR_LR:
1665 stmt( IRStmt_Put( OFFB_LR, src ) );
1666 break;
1667 case PPC32_SPR_CTR:
1668 stmt( IRStmt_Put( OFFB_CTR, src ) );
1669 break;
1670 case PPC32_SPR_VRSAVE:
1671 stmt( IRStmt_Put( OFFB_VRSAVE, src ) );
1672 break;
cerion225a0342005-09-12 20:49:09 +00001673 case PPC32_SPR_VSCR:
cerionedf7fc52005-11-18 20:57:41 +00001674 stmt( IRStmt_Put( OFFB_VSCR,
1675 binop(Iop_And32, src,
1676 mkU32(MASK_VSCR_VALID)) ) );
cerion225a0342005-09-12 20:49:09 +00001677 break;
cerionedf7fc52005-11-18 20:57:41 +00001678 case PPC32_SPR_XER:
1679 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
1680 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
1681 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
1682 putXER_BC( unop(Iop_32to8, src) );
1683 break;
1684
sewardjb51f0f42005-07-18 11:38:02 +00001685 default:
1686 vpanic("putSPR(ppc32)");
cerion3007c7f2005-02-23 23:13:29 +00001687 }
cerion76222262005-02-05 13:45:57 +00001688}
cerion38674602005-02-08 02:19:25 +00001689
cerion76222262005-02-05 13:45:57 +00001690
1691
cerione9d361a2005-03-04 17:35:29 +00001692/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00001693/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00001694/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00001695
cerion91ad5362005-01-27 23:02:41 +00001696/*
1697 Integer Arithmetic Instructions
1698*/
cerion645c9302005-01-31 10:09:59 +00001699static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00001700{
cerion76de5cf2005-11-18 18:25:12 +00001701 /* D-Form, XO-Form */
1702 UChar opc1 = ifieldOPC(theInstr);
1703 UChar rD_addr = ifieldRegDS(theInstr);
1704 UChar rA_addr = ifieldRegA(theInstr);
1705 Int simm16 = ifieldSIMM16(theInstr);
1706 UChar rB_addr = ifieldRegB(theInstr);
1707 UChar flag_OE = ifieldBIT10(theInstr);
1708 UInt opc2 = ifieldOPClo9(theInstr);
1709 UChar flag_rC = ifieldBIT0(theInstr);
1710
1711 IRTemp rA = newTemp(Ity_I32);
1712 IRTemp rB = newTemp(Ity_I32);
1713 IRTemp rD = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00001714 IRTemp res64 = newTemp(Ity_I64); // multiplies need this.
cerion70e24122005-03-16 00:27:37 +00001715
cerionb85e8bb2005-02-16 08:54:33 +00001716 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00001717
cerion76de5cf2005-11-18 18:25:12 +00001718 assign( rA, getIReg(rA_addr) );
1719 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00001720
cerionb85e8bb2005-02-16 08:54:33 +00001721 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00001722 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00001723 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
cerion76de5cf2005-11-18 18:25:12 +00001724 DIP("addic r%d,r%d,%d\n", rD_addr, rA_addr, simm16);
1725 assign( rD, binop( Iop_Add32, mkexpr(rA), mkU32(simm16) ) );
sewardjb51f0f42005-07-18 11:38:02 +00001726 set_XER_CA( PPC32G_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00001727 mkexpr(rD), mkexpr(rA), mkU32(simm16),
sewardjb51f0f42005-07-18 11:38:02 +00001728 mkU32(0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00001729 break;
sewardjb51f0f42005-07-18 11:38:02 +00001730
cerione9d361a2005-03-04 17:35:29 +00001731 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
cerion76de5cf2005-11-18 18:25:12 +00001732 DIP("addic. r%d,r%d,%d\n", rD_addr, rA_addr, simm16);
1733 assign( rD, binop( Iop_Add32, mkexpr(rA), mkU32(simm16) ) );
sewardjb51f0f42005-07-18 11:38:02 +00001734 set_XER_CA( PPC32G_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00001735 mkexpr(rD), mkexpr(rA), mkU32(simm16),
sewardjb51f0f42005-07-18 11:38:02 +00001736 mkU32(0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00001737 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00001738 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00001739 break;
1740
cerione9d361a2005-03-04 17:35:29 +00001741 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00001742 // li rD,val == addi rD,0,val
1743 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00001744 if ( rA_addr == 0 ) {
1745 DIP("li r%d,%d\n", rD_addr, simm16);
1746 assign( rD, mkU32(simm16) );
cerionb85e8bb2005-02-16 08:54:33 +00001747 } else {
cerion76de5cf2005-11-18 18:25:12 +00001748 DIP("addi r%d,r%d,%d\n", rD_addr, rA_addr, simm16);
1749 assign( rD, binop( Iop_Add32, mkexpr(rA), mkU32(simm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00001750 }
1751 break;
cerion91ad5362005-01-27 23:02:41 +00001752
cerione9d361a2005-03-04 17:35:29 +00001753 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00001754 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00001755 if ( rA_addr == 0 ) {
1756 DIP("lis r%d,%d\n", rD_addr, simm16);
1757 assign( rD, mkU32(simm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00001758 } else {
cerion76de5cf2005-11-18 18:25:12 +00001759 DIP("addis r%d,r%d,0x%x\n", rD_addr, rA_addr, simm16);
1760 assign( rD, binop(Iop_Add32, mkexpr(rA), mkU32(simm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00001761 }
1762 break;
cerion91ad5362005-01-27 23:02:41 +00001763
cerione9d361a2005-03-04 17:35:29 +00001764 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
cerion76de5cf2005-11-18 18:25:12 +00001765 DIP("mulli r%d,r%d,%d\n", rD_addr, rA_addr, simm16);
1766 assign( res64, binop(Iop_MullS32, mkexpr(rA), mkU32(simm16)) );
1767 assign( rD, unop(Iop_64to32, mkexpr(res64)) );
cerionb85e8bb2005-02-16 08:54:33 +00001768 break;
cerion38674602005-02-08 02:19:25 +00001769
cerione9d361a2005-03-04 17:35:29 +00001770 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
cerion76de5cf2005-11-18 18:25:12 +00001771 DIP("subfic r%d,r%d,%d\n", rD_addr, rA_addr, simm16);
1772 // rD = simm16 - rA
1773 assign( rD, binop(Iop_Sub32, mkU32(simm16), mkexpr(rA)) );
sewardjb51f0f42005-07-18 11:38:02 +00001774 set_XER_CA( PPC32G_FLAG_OP_SUBFI,
cerion76de5cf2005-11-18 18:25:12 +00001775 mkexpr(rD), mkexpr(rA), mkU32(simm16),
sewardjb51f0f42005-07-18 11:38:02 +00001776 mkU32(0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00001777 break;
cerion38674602005-02-08 02:19:25 +00001778
cerionb85e8bb2005-02-16 08:54:33 +00001779 /* XO-Form */
1780 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00001781 do_rc = True; // All below record to CR
1782
1783 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00001784 case 0x10A: // add (Add, PPC32 p347)
cerionb85e8bb2005-02-16 08:54:33 +00001785 DIP("add%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001786 flag_OE ? "o" : "", flag_rC ? "." : "",
1787 rD_addr, rA_addr, rB_addr);
1788 assign( rD, binop(Iop_Add32, mkexpr(rA), mkexpr(rB)) );
cerion70e24122005-03-16 00:27:37 +00001789 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001790 set_XER_OV( PPC32G_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00001791 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001792 }
cerionb85e8bb2005-02-16 08:54:33 +00001793 break;
cerion91ad5362005-01-27 23:02:41 +00001794
cerione9d361a2005-03-04 17:35:29 +00001795 case 0x00A: // addc (Add Carrying, PPC32 p348)
cerionb85e8bb2005-02-16 08:54:33 +00001796 DIP("addc%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001797 flag_OE ? "o" : "", flag_rC ? "." : "",
1798 rD_addr, rA_addr, rB_addr);
1799 assign( rD, binop(Iop_Add32, mkexpr(rA), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00001800 set_XER_CA( PPC32G_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00001801 mkexpr(rD), mkexpr(rA), mkexpr(rB),
sewardjb51f0f42005-07-18 11:38:02 +00001802 mkU32(0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00001803 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001804 set_XER_OV( PPC32G_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00001805 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001806 }
cerionb85e8bb2005-02-16 08:54:33 +00001807 break;
1808
sewardjb51f0f42005-07-18 11:38:02 +00001809 case 0x08A: { // adde (Add Extended, PPC32 p349)
1810 IRTemp old_xer_ca = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00001811 DIP("adde%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001812 flag_OE ? "o" : "", flag_rC ? "." : "",
1813 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00001814 // rD = rA + rB + XER[CA]
cerionedf7fc52005-11-18 20:57:41 +00001815 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00001816 assign( rD, binop(Iop_Add32, mkexpr(rA),
1817 binop(Iop_Add32, mkexpr(rB), mkexpr(old_xer_ca))) );
sewardjb51f0f42005-07-18 11:38:02 +00001818 set_XER_CA( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001819 mkexpr(rD), mkexpr(rA), mkexpr(rB),
sewardjb51f0f42005-07-18 11:38:02 +00001820 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00001821 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001822 set_XER_OV( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001823 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001824 }
cerionb85e8bb2005-02-16 08:54:33 +00001825 break;
sewardjb51f0f42005-07-18 11:38:02 +00001826 }
1827
1828 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
1829 IRTemp old_xer_ca = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00001830 if (rB_addr != 0) {
1831 vex_printf("dis_int_arith(PPC32)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00001832 return False;
1833 }
1834 DIP("addme%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001835 flag_OE ? "o" : "", flag_rC ? "." : "",
1836 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00001837 // rD = rA + (-1) + XER[CA]
1838 // => Just another form of adde
cerionedf7fc52005-11-18 20:57:41 +00001839 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00001840 assign( rD, binop(Iop_Add32, mkexpr(rA),
sewardjb51f0f42005-07-18 11:38:02 +00001841 binop(Iop_Add32, mkU32(-1), mkexpr(old_xer_ca)) ));
1842 set_XER_CA( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001843 mkexpr(rD), mkexpr(rA), mkU32(-1),
sewardjb51f0f42005-07-18 11:38:02 +00001844 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00001845 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001846 set_XER_OV( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001847 mkexpr(rD), mkexpr(rA), mkU32(-1) );
cerion70e24122005-03-16 00:27:37 +00001848 }
cerionb85e8bb2005-02-16 08:54:33 +00001849 break;
sewardjb51f0f42005-07-18 11:38:02 +00001850 }
1851
1852 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
1853 IRTemp old_xer_ca = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00001854 if (rB_addr != 0) {
1855 vex_printf("dis_int_arith(PPC32)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00001856 return False;
1857 }
1858 DIP("addze%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001859 flag_OE ? "o" : "", flag_rC ? "." : "",
1860 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00001861 // rD = rA + (0) + XER[CA]
1862 // => Just another form of adde
cerionedf7fc52005-11-18 20:57:41 +00001863 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00001864 assign( rD, binop(Iop_Add32, mkexpr(rA), mkexpr(old_xer_ca)) );
sewardjb51f0f42005-07-18 11:38:02 +00001865 set_XER_CA( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001866 mkexpr(rD), mkexpr(rA), mkU32(0),
sewardjb51f0f42005-07-18 11:38:02 +00001867 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00001868 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001869 set_XER_OV( PPC32G_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00001870 mkexpr(rD), mkexpr(rA), mkU32(0) );
cerion70e24122005-03-16 00:27:37 +00001871 }
cerionb85e8bb2005-02-16 08:54:33 +00001872 break;
sewardjb51f0f42005-07-18 11:38:02 +00001873 }
cerion91ad5362005-01-27 23:02:41 +00001874
cerione9d361a2005-03-04 17:35:29 +00001875 case 0x1EB: // divw (Divide Word, PPC32 p388)
cerionb85e8bb2005-02-16 08:54:33 +00001876 DIP("divw%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001877 flag_OE ? "o" : "", flag_rC ? "." : "",
1878 rD_addr, rA_addr, rB_addr);
1879 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
cerion70e24122005-03-16 00:27:37 +00001880 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001881 set_XER_OV( PPC32G_FLAG_OP_DIVW,
cerion76de5cf2005-11-18 18:25:12 +00001882 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001883 }
cerionb85e8bb2005-02-16 08:54:33 +00001884 /* Note:
1885 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00001886 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00001887 => But _no_ exception raised. */
1888 break;
cerion91ad5362005-01-27 23:02:41 +00001889
cerione9d361a2005-03-04 17:35:29 +00001890 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
cerionb85e8bb2005-02-16 08:54:33 +00001891 DIP("divwu%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001892 flag_OE ? "o" : "", flag_rC ? "." : "",
1893 rD_addr, rA_addr, rB_addr);
1894 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
cerion70e24122005-03-16 00:27:37 +00001895 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001896 set_XER_OV( PPC32G_FLAG_OP_DIVWU,
cerion76de5cf2005-11-18 18:25:12 +00001897 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001898 }
cerionb85e8bb2005-02-16 08:54:33 +00001899 /* Note: ditto comment divw, for (x / 0) */
1900 break;
cerion91ad5362005-01-27 23:02:41 +00001901
cerione9d361a2005-03-04 17:35:29 +00001902 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00001903 if (flag_OE != 0) {
1904 vex_printf("dis_int_arith(PPC32)(mulhw,flag_OE)\n");
1905 return False;
1906 }
cerion76de5cf2005-11-18 18:25:12 +00001907 DIP("mulhw%s r%d,r%d,r%d\n", flag_rC ? "." : "",
1908 rD_addr, rA_addr, rB_addr);
1909 assign( res64, binop(Iop_MullS32, mkexpr(rA), mkexpr(rB)) );
1910 assign( rD, unop(Iop_64HIto32, mkexpr(res64)) );
cerionb85e8bb2005-02-16 08:54:33 +00001911 break;
cerionc19d5e12005-02-01 15:56:25 +00001912
cerione9d361a2005-03-04 17:35:29 +00001913 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00001914 if (flag_OE != 0) {
1915 vex_printf("dis_int_arith(PPC32)(mulhwu,flag_OE)\n");
1916 return False;
1917 }
cerion76de5cf2005-11-18 18:25:12 +00001918 DIP("mulhwu%s r%d,r%d,r%d\n", flag_rC ? "." : "",
1919 rD_addr, rA_addr, rB_addr);
1920 assign( res64, binop(Iop_MullU32, mkexpr(rA), mkexpr(rB)) );
1921 assign( rD, unop(Iop_64HIto32, mkexpr(res64)) );
cerionb85e8bb2005-02-16 08:54:33 +00001922 break;
1923
cerione9d361a2005-03-04 17:35:29 +00001924 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
cerionb85e8bb2005-02-16 08:54:33 +00001925 DIP("mullw%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001926 flag_OE ? "o" : "", flag_rC ? "." : "",
1927 rD_addr, rA_addr, rB_addr);
1928 assign( res64, binop(Iop_MullU32, mkexpr(rA), mkexpr(rB)) );
1929 assign( rD, unop(Iop_64to32, mkexpr(res64)) );
cerion70e24122005-03-16 00:27:37 +00001930 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001931 set_XER_OV( PPC32G_FLAG_OP_MULLW,
cerion76de5cf2005-11-18 18:25:12 +00001932 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001933 }
cerionb85e8bb2005-02-16 08:54:33 +00001934 break;
cerionc19d5e12005-02-01 15:56:25 +00001935
cerione9d361a2005-03-04 17:35:29 +00001936 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00001937 if (rB_addr != 0) {
1938 vex_printf("dis_int_arith(PPC32)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00001939 return False;
1940 }
1941 DIP("neg%s%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001942 flag_OE ? "o" : "", flag_rC ? "." : "",
1943 rD_addr, rA_addr);
cerionb85e8bb2005-02-16 08:54:33 +00001944 // rD = (log not)rA + 1
cerion76de5cf2005-11-18 18:25:12 +00001945 assign( rD, binop(Iop_Add32,
1946 unop(Iop_Not32, mkexpr(rA)), mkU32(1)) );
cerion70e24122005-03-16 00:27:37 +00001947 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001948 set_XER_OV( PPC32G_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00001949 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001950 }
cerionb85e8bb2005-02-16 08:54:33 +00001951 break;
cerion91ad5362005-01-27 23:02:41 +00001952
cerione9d361a2005-03-04 17:35:29 +00001953 case 0x028: // subf (Subtract From, PPC32 p537)
cerionb85e8bb2005-02-16 08:54:33 +00001954 DIP("subf%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001955 flag_OE ? "o" : "", flag_rC ? "." : "",
1956 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00001957 // rD = rB - rA
cerion76de5cf2005-11-18 18:25:12 +00001958 assign( rD, binop(Iop_Sub32, mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00001959 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001960 set_XER_OV( PPC32G_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00001961 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001962 }
cerionb85e8bb2005-02-16 08:54:33 +00001963 break;
cerion38674602005-02-08 02:19:25 +00001964
cerione9d361a2005-03-04 17:35:29 +00001965 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
cerionb85e8bb2005-02-16 08:54:33 +00001966 DIP("subfc%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001967 flag_OE ? "o" : "", flag_rC ? "." : "",
1968 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00001969 // rD = rB - rA
cerion76de5cf2005-11-18 18:25:12 +00001970 assign( rD, binop(Iop_Sub32, mkexpr(rB), mkexpr(rA)) );
sewardjb51f0f42005-07-18 11:38:02 +00001971 set_XER_CA( PPC32G_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00001972 mkexpr(rD), mkexpr(rA), mkexpr(rB),
sewardjb51f0f42005-07-18 11:38:02 +00001973 mkU32(0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00001974 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001975 set_XER_OV( PPC32G_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00001976 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001977 }
cerionb85e8bb2005-02-16 08:54:33 +00001978 break;
1979
sewardjb51f0f42005-07-18 11:38:02 +00001980 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
1981 IRTemp old_xer_ca = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00001982 DIP("subfe%s%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00001983 flag_OE ? "o" : "", flag_rC ? "." : "",
1984 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00001985 // rD = (log not)rA + rB + XER[CA]
cerionedf7fc52005-11-18 20:57:41 +00001986 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00001987 assign( rD, binop(Iop_Add32, unop(Iop_Not32, mkexpr(rA)),
1988 binop(Iop_Add32, mkexpr(rB), mkexpr(old_xer_ca))) );
sewardjb51f0f42005-07-18 11:38:02 +00001989 set_XER_CA( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00001990 mkexpr(rD), mkexpr(rA), mkexpr(rB),
sewardjb51f0f42005-07-18 11:38:02 +00001991 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00001992 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00001993 set_XER_OV( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00001994 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00001995 }
cerionb85e8bb2005-02-16 08:54:33 +00001996 break;
sewardjb51f0f42005-07-18 11:38:02 +00001997 }
1998
sewardj20ef5472005-07-21 14:48:31 +00001999 case 0x0E8: { // subfme (Subtract from Minus One Extended, PPC32 p541)
2000 IRTemp old_xer_ca = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002001 if (rB_addr != 0) {
2002 vex_printf("dis_int_arith(PPC32)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002003 return False;
2004 }
2005 DIP("subfme%s%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002006 flag_OE ? "o" : "", flag_rC ? "." : "",
2007 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002008 // rD = (log not)rA + (-1) + XER[CA]
2009 // => Just another form of subfe
cerionedf7fc52005-11-18 20:57:41 +00002010 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00002011 assign( rD, binop(Iop_Add32, unop(Iop_Not32, mkexpr(rA)),
sewardj20ef5472005-07-21 14:48:31 +00002012 binop(Iop_Add32, mkU32(-1), mkexpr(old_xer_ca))) );
2013 set_XER_CA( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002014 mkexpr(rD), mkexpr(rA), mkU32(-1),
sewardj20ef5472005-07-21 14:48:31 +00002015 mkexpr(old_xer_ca) );
2016 if (flag_OE) {
2017 set_XER_OV( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002018 mkexpr(rD), mkexpr(rA), mkU32(-1) );
sewardj20ef5472005-07-21 14:48:31 +00002019 }
2020 break;
2021 }
2022
sewardjb51f0f42005-07-18 11:38:02 +00002023 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
2024 IRTemp old_xer_ca = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002025 if (rB_addr != 0) {
2026 vex_printf("dis_int_arith(PPC32)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002027 return False;
2028 }
2029 DIP("subfze%s%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002030 flag_OE ? "o" : "", flag_rC ? "." : "",
2031 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002032 // rD = (log not)rA + (0) + XER[CA]
2033 // => Just another form of subfe
cerionedf7fc52005-11-18 20:57:41 +00002034 assign( old_xer_ca, getXER_CA32() );
cerion76de5cf2005-11-18 18:25:12 +00002035 assign( rD, binop(Iop_Add32,
2036 unop(Iop_Not32, mkexpr(rA)), mkexpr(old_xer_ca)) );
sewardjb51f0f42005-07-18 11:38:02 +00002037 set_XER_CA( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002038 mkexpr(rD), mkexpr(rA), mkU32(0),
sewardjb51f0f42005-07-18 11:38:02 +00002039 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002040 if (flag_OE) {
sewardj20ef5472005-07-21 14:48:31 +00002041 set_XER_OV( PPC32G_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002042 mkexpr(rD), mkexpr(rA), mkU32(0) );
cerion70e24122005-03-16 00:27:37 +00002043 }
cerionb85e8bb2005-02-16 08:54:33 +00002044 break;
sewardjb51f0f42005-07-18 11:38:02 +00002045 }
cerionae694622005-01-28 17:52:47 +00002046
cerionb85e8bb2005-02-16 08:54:33 +00002047 default:
2048 vex_printf("dis_int_arith(PPC32)(opc2)\n");
2049 return False;
2050 }
2051 break;
2052 default:
2053 vex_printf("dis_int_arith(PPC32)(opc1)\n");
2054 return False;
2055 }
cerion91ad5362005-01-27 23:02:41 +00002056
cerion76de5cf2005-11-18 18:25:12 +00002057 putIReg( rD_addr, mkexpr(rD) );
2058
2059 if (do_rc && flag_rC) {
2060 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002061 }
2062 return True;
cerion91ad5362005-01-27 23:02:41 +00002063}
2064
2065
2066
cerion3d870a32005-03-18 12:23:33 +00002067/*
2068 Integer Compare Instructions
2069*/
cerion7aa4bbc2005-01-29 09:32:07 +00002070static Bool dis_int_cmp ( UInt theInstr )
2071{
cerion76de5cf2005-11-18 18:25:12 +00002072 /* D-Form, X-Form */
2073 UChar opc1 = ifieldOPC(theInstr);
2074 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2075 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2076 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2077 UChar rA_addr = ifieldRegA(theInstr);
2078 UInt UIMM_16 = ifieldUIMM16(theInstr);
2079 UChar rB_addr = ifieldRegB(theInstr);
2080 UInt opc2 = ifieldOPClo10(theInstr);
2081 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002082
cerion76de5cf2005-11-18 18:25:12 +00002083 Int EXTS_SIMM = extend_s_16to32(UIMM_16);
2084 IRTemp rA = newTemp(Ity_I32);
2085 IRTemp rB = newTemp(Ity_I32);
2086
2087 assign( rA, getIReg(rA_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00002088
2089 if (flag_L==1) { // L==1 invalid for 32 bit.
2090 vex_printf("dis_int_cmp(PPC32)(flag_L)\n");
2091 return False;
2092 }
2093
cerion76de5cf2005-11-18 18:25:12 +00002094 if (b22 != 0) {
2095 vex_printf("dis_int_cmp(PPC32)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002096 return False;
2097 }
2098
2099 switch (opc1) {
sewardjb51f0f42005-07-18 11:38:02 +00002100 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
cerion76de5cf2005-11-18 18:25:12 +00002101 DIP("cmp cr%d,r%d,%d\n", crfD, rA_addr, EXTS_SIMM);
sewardjb51f0f42005-07-18 11:38:02 +00002102 putCR321( crfD, unop(Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00002103 binop(Iop_CmpORD32S, mkexpr(rA),
sewardjb51f0f42005-07-18 11:38:02 +00002104 mkU32(EXTS_SIMM))) );
2105 putCR0( crfD, getXER_SO() );
2106 break;
2107
2108 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
cerion76de5cf2005-11-18 18:25:12 +00002109 DIP("cmpli cr%d,r%d,0x%x\n", crfD, rA_addr, UIMM_16);
sewardjb51f0f42005-07-18 11:38:02 +00002110 putCR321( crfD, unop(Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00002111 binop(Iop_CmpORD32U, mkexpr(rA),
sewardjb51f0f42005-07-18 11:38:02 +00002112 mkU32(UIMM_16))) );
2113 putCR0( crfD, getXER_SO() );
2114 break;
cerionb85e8bb2005-02-16 08:54:33 +00002115
2116 /* X Form */
2117 case 0x1F:
2118 if (b0 != 0) {
2119 vex_printf("dis_int_cmp(PPC32)(0x1F,b0)\n");
2120 return False;
2121 }
cerion76de5cf2005-11-18 18:25:12 +00002122 assign( rB, getIReg(rB_addr) );
cerion7aa4bbc2005-01-29 09:32:07 +00002123
cerionb85e8bb2005-02-16 08:54:33 +00002124 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00002125 case 0x000: // cmp (Compare, PPC32 p367)
cerion76de5cf2005-11-18 18:25:12 +00002126 DIP("cmp cr%d,r%d,r%d\n", crfD, rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00002127 putCR321( crfD, unop(Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00002128 binop(Iop_CmpORD32S, mkexpr(rA), mkexpr(rB))) );
sewardjb51f0f42005-07-18 11:38:02 +00002129 putCR0( crfD, getXER_SO() );
2130 break;
cerionb85e8bb2005-02-16 08:54:33 +00002131
sewardjb51f0f42005-07-18 11:38:02 +00002132 case 0x020: // cmpl (Compare Logical, PPC32 p369)
cerion76de5cf2005-11-18 18:25:12 +00002133 DIP("cmpl cr%d,r%d,r%d\n", crfD, rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00002134 putCR321( crfD, unop(Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00002135 binop(Iop_CmpORD32U, mkexpr(rA), mkexpr(rB))) );
sewardjb51f0f42005-07-18 11:38:02 +00002136 putCR0( crfD, getXER_SO() );
2137 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002138
sewardjb51f0f42005-07-18 11:38:02 +00002139 default:
2140 vex_printf("dis_int_cmp(PPC32)(opc2)\n");
2141 return False;
cerionb85e8bb2005-02-16 08:54:33 +00002142 }
2143 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002144
cerionb85e8bb2005-02-16 08:54:33 +00002145 default:
2146 vex_printf("dis_int_cmp(PPC32)(opc1)\n");
2147 return False;
2148 }
2149
cerionb85e8bb2005-02-16 08:54:33 +00002150 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00002151}
2152
2153
cerion3d870a32005-03-18 12:23:33 +00002154/*
2155 Integer Logical Instructions
2156*/
cerion7aa4bbc2005-01-29 09:32:07 +00002157static Bool dis_int_logic ( UInt theInstr )
2158{
cerion76de5cf2005-11-18 18:25:12 +00002159 /* D-Form, X-Form */
2160 UChar opc1 = ifieldOPC(theInstr);
2161 UChar rS_addr = ifieldRegDS(theInstr);
2162 UChar rA_addr = ifieldRegA(theInstr);
2163 UInt UIMM_16 = ifieldUIMM16(theInstr);
2164 UChar rB_addr = ifieldRegB(theInstr);
2165 UInt opc2 = ifieldOPClo10(theInstr);
2166 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00002167
2168 Bool do_rc = False;
2169
cerion76de5cf2005-11-18 18:25:12 +00002170 IRTemp rS = newTemp(Ity_I32);
2171 IRTemp rA = newTemp(Ity_I32);
2172 IRTemp rB = newTemp(Ity_I32);
cerione9d361a2005-03-04 17:35:29 +00002173 IRExpr* irx;
cerionb85e8bb2005-02-16 08:54:33 +00002174
cerion76de5cf2005-11-18 18:25:12 +00002175 assign( rS, getIReg(rS_addr) );
2176 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00002177
2178 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00002179 case 0x1C: // andi. (AND Immediate, PPC32 p358)
cerion76de5cf2005-11-18 18:25:12 +00002180 DIP("andi. r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2181 assign( rA, binop(Iop_And32, mkexpr(rS), mkU32(UIMM_16)) );
cerion70e24122005-03-16 00:27:37 +00002182 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002183 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00002184 break;
2185
cerione9d361a2005-03-04 17:35:29 +00002186 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
cerion76de5cf2005-11-18 18:25:12 +00002187 DIP("andis r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2188 assign( rA, binop(Iop_And32, mkexpr(rS), mkU32(UIMM_16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00002189 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002190 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00002191 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002192
cerione9d361a2005-03-04 17:35:29 +00002193 case 0x18: // ori (OR Immediate, PPC32 p497)
cerion76de5cf2005-11-18 18:25:12 +00002194 DIP("ori r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2195 assign( rA, binop(Iop_Or32, mkexpr(rS), mkU32(UIMM_16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002196 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002197
cerione9d361a2005-03-04 17:35:29 +00002198 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
cerion76de5cf2005-11-18 18:25:12 +00002199 DIP("oris r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2200 assign( rA, binop(Iop_Or32, mkexpr(rS), mkU32(UIMM_16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002201 break;
cerionaabdfbf2005-01-29 12:56:15 +00002202
cerione9d361a2005-03-04 17:35:29 +00002203 case 0x1A: // xori (XOR Immediate, PPC32 p550)
cerion76de5cf2005-11-18 18:25:12 +00002204 DIP("xori r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2205 assign( rA, binop(Iop_Xor32, mkexpr(rS), mkU32(UIMM_16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002206 break;
cerion38674602005-02-08 02:19:25 +00002207
cerione9d361a2005-03-04 17:35:29 +00002208 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
cerion76de5cf2005-11-18 18:25:12 +00002209 DIP("xoris r%d,r%d,0x%x\n", rA_addr, rS_addr, UIMM_16);
2210 assign( rA, binop(Iop_Xor32, mkexpr(rS), mkU32(UIMM_16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002211 break;
cerionaabdfbf2005-01-29 12:56:15 +00002212
cerionb85e8bb2005-02-16 08:54:33 +00002213 /* X Form */
2214 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00002215 do_rc = True; // All below record to CR
2216
cerionb85e8bb2005-02-16 08:54:33 +00002217 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00002218 case 0x01C: // and (AND, PPC32 p356)
2219 DIP("and%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002220 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2221 assign(rA, binop(Iop_And32, mkexpr(rS), mkexpr(rB)));
sewardjb51f0f42005-07-18 11:38:02 +00002222 break;
cerionb85e8bb2005-02-16 08:54:33 +00002223
sewardjb51f0f42005-07-18 11:38:02 +00002224 case 0x03C: // andc (AND with Complement, PPC32 p357)
2225 DIP("andc%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002226 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2227 assign(rA, binop(Iop_And32, mkexpr(rS),
2228 unop(Iop_Not32, mkexpr(rB))));
sewardjb51f0f42005-07-18 11:38:02 +00002229 break;
2230
2231 case 0x01A: // cntlzw (Count Leading Zeros Word, PPC32 p371)
cerion76de5cf2005-11-18 18:25:12 +00002232 if (rB_addr!=0) {
2233 vex_printf("dis_int_logic(PPC32)(cntlzw,rB_addr)\n");
sewardjb51f0f42005-07-18 11:38:02 +00002234 return False;
2235 }
2236 DIP("cntlzw%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002237 flag_rC ? "." : "", rA_addr, rS_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002238
sewardjb51f0f42005-07-18 11:38:02 +00002239 // Iop_Clz32 undefined for arg==0, so deal with that case:
cerion76de5cf2005-11-18 18:25:12 +00002240 irx = binop(Iop_CmpNE32, mkexpr(rS), mkU32(0));
2241 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
sewardjb51f0f42005-07-18 11:38:02 +00002242 mkU32(32),
cerion76de5cf2005-11-18 18:25:12 +00002243 unop(Iop_Clz32, mkexpr(rS)) ));
2244 // alternatively: assign(rA, verbose_Clz32(rS));
sewardjb51f0f42005-07-18 11:38:02 +00002245 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002246
sewardj20ef5472005-07-21 14:48:31 +00002247 case 0x11C: // eqv (Equivalent, PPC32 p396)
2248 DIP("eqv%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002249 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2250 assign( rA, unop(Iop_Not32, binop(Iop_Xor32,
2251 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00002252 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002253
cerione9d361a2005-03-04 17:35:29 +00002254 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00002255 if (rB_addr!=0) {
2256 vex_printf("dis_int_logic(PPC32)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002257 return False;
2258 }
2259 DIP("extsb%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002260 flag_rC ? "." : "", rA_addr, rS_addr);
2261 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00002262 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002263
cerione9d361a2005-03-04 17:35:29 +00002264 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00002265 if (rB_addr!=0) {
2266 vex_printf("dis_int_logic(PPC32)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002267 return False;
2268 }
2269 DIP("extsh%s r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002270 flag_rC ? "." : "", rA_addr, rS_addr);
2271 assign( rA, unop(Iop_16Sto32, unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00002272 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002273
cerione9d361a2005-03-04 17:35:29 +00002274 case 0x1DC: // nand (NAND, PPC32 p492)
cerionb85e8bb2005-02-16 08:54:33 +00002275 DIP("nand%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002276 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2277 assign( rA, unop(Iop_Not32,
2278 binop(Iop_And32, mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00002279 break;
2280
cerione9d361a2005-03-04 17:35:29 +00002281 case 0x07C: // nor (NOR, PPC32 p494)
cerionb85e8bb2005-02-16 08:54:33 +00002282 DIP("nor%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002283 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2284 assign( rA, unop(Iop_Not32,
2285 binop(Iop_Or32, mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00002286 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002287
cerione9d361a2005-03-04 17:35:29 +00002288 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00002289 if ((!flag_rC) && rS_addr == rB_addr) {
2290 DIP("mr r%d,r%d\n", rA_addr, rS_addr);
2291 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00002292 } else {
2293 DIP("or%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002294 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2295 assign( rA, binop(Iop_Or32, mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00002296 }
cerionb85e8bb2005-02-16 08:54:33 +00002297 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002298
cerione9d361a2005-03-04 17:35:29 +00002299 case 0x19C: // orc (OR with Complement, PPC32 p496)
cerionb85e8bb2005-02-16 08:54:33 +00002300 DIP("orc%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002301 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2302 assign( rA, binop(Iop_Or32, mkexpr(rS),
2303 unop(Iop_Not32, mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00002304 break;
2305
cerione9d361a2005-03-04 17:35:29 +00002306 case 0x13C: // xor (XOR, PPC32 p549)
cerionb85e8bb2005-02-16 08:54:33 +00002307 DIP("xor%s r%d,r%d,r%d\n",
cerion76de5cf2005-11-18 18:25:12 +00002308 flag_rC ? "." : "", rA_addr, rS_addr, rB_addr);
2309 assign( rA, binop(Iop_Xor32, mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00002310 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002311
cerionb85e8bb2005-02-16 08:54:33 +00002312 default:
2313 vex_printf("dis_int_logic(PPC32)(opc2)\n");
2314 return False;
2315 }
cerionb85e8bb2005-02-16 08:54:33 +00002316 break;
2317
2318 default:
2319 vex_printf("dis_int_logic(PPC32)(opc1)\n");
2320 return False;
2321 }
cerion70e24122005-03-16 00:27:37 +00002322
cerion76de5cf2005-11-18 18:25:12 +00002323 putIReg( rA_addr, mkexpr(rA) );
2324
2325 if (do_rc && flag_rC) {
2326 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00002327 }
2328 return True;
cerion645c9302005-01-31 10:09:59 +00002329}
2330
2331
2332
cerion3d870a32005-03-18 12:23:33 +00002333/*
2334 Integer Rotate Instructions
2335*/
cerion645c9302005-01-31 10:09:59 +00002336static Bool dis_int_rot ( UInt theInstr )
2337{
cerionb85e8bb2005-02-16 08:54:33 +00002338 /* M-Form */
cerion76de5cf2005-11-18 18:25:12 +00002339 UChar opc1 = ifieldOPC(theInstr);
2340 UChar rS_addr = ifieldRegDS(theInstr);
2341 UChar rA_addr = ifieldRegA(theInstr);
2342 UChar rB_addr = ifieldRegB(theInstr);
2343 UChar sh_imm = rB_addr;
2344 UChar MaskBegin = toUChar( IFIELD( theInstr, 6, 5 ) );
2345 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
2346 UChar flag_rC = ifieldBIT0(theInstr);
2347
cerion239e2e42005-02-24 16:59:17 +00002348 UInt mask = MASK(31-MaskEnd, 31-MaskBegin);
cerion76de5cf2005-11-18 18:25:12 +00002349 IRTemp rS = newTemp(Ity_I32);
2350 IRTemp rA = newTemp(Ity_I32);
2351 IRTemp rB = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00002352
cerion76de5cf2005-11-18 18:25:12 +00002353 assign( rS, getIReg(rS_addr) );
2354 assign( rB, getIReg(rB_addr) );
cerione9d361a2005-03-04 17:35:29 +00002355
cerionb85e8bb2005-02-16 08:54:33 +00002356 switch (opc1) {
sewardjc9659532005-07-21 21:33:57 +00002357 case 0x14:
2358 // rlwimi (Rotate Left Word Immediate then Mask Insert, PPC32 p500)
cerion76de5cf2005-11-18 18:25:12 +00002359 DIP("rlwimi%s r%d,r%d,%d,%d,%d\n", flag_rC ? "." : "",
2360 rA_addr, rS_addr, sh_imm, MaskBegin, MaskEnd);
2361 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
2362 assign( rA, binop(Iop_Or32,
cerionb85e8bb2005-02-16 08:54:33 +00002363 binop(Iop_And32, mkU32(mask),
cerion76de5cf2005-11-18 18:25:12 +00002364 ROTL32(mkexpr(rS), mkU32(sh_imm))),
2365 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask))) );
cerionb85e8bb2005-02-16 08:54:33 +00002366 break;
cerion645c9302005-01-31 10:09:59 +00002367
sewardjc9659532005-07-21 21:33:57 +00002368 case 0x15:
2369 // rlwinm (Rotate Left Word Immediate then AND with Mask, PPC32 p501)
sewardj26b33202005-10-07 09:45:16 +00002370 vassert(MaskBegin < 32);
2371 vassert(MaskEnd < 32);
2372 vassert(sh_imm < 32);
2373
2374 if (MaskBegin == 0 && sh_imm+MaskEnd == 31) {
2375 /* Special-case the ,n,0,31-n form as that is just n-bit
2376 shift left (PPC32 p501) */
cerion76de5cf2005-11-18 18:25:12 +00002377 DIP("slwi%s r%d,r%d,%d\n", flag_rC ? "." : "",
2378 rA_addr, rS_addr, sh_imm);
2379 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
sewardj26b33202005-10-07 09:45:16 +00002380 }
2381 else
2382 if (MaskEnd == 31 && sh_imm+MaskBegin == 32) {
2383 /* Special-case the ,32-n,n,31 form as that is just n-bit
2384 unsigned shift right (PPC32 p501) */
cerion76de5cf2005-11-18 18:25:12 +00002385 DIP("srwi%s r%d,r%d,%d\n", flag_rC ? "." : "",
2386 rA_addr, rS_addr, sh_imm);
2387 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBegin)) );
sewardj26b33202005-10-07 09:45:16 +00002388 }
2389 else {
2390 /* General case. */
cerion76de5cf2005-11-18 18:25:12 +00002391 DIP("rlwinm%s r%d,r%d,%d,%d,%d\n", flag_rC ? "." : "",
2392 rA_addr, rS_addr, sh_imm, MaskBegin, MaskEnd);
2393 // rA = ROTL(rS, Imm) & mask
2394 assign( rA, binop(Iop_And32, ROTL32(mkexpr(rS), mkU32(sh_imm)),
sewardj26b33202005-10-07 09:45:16 +00002395 mkU32(mask)) );
2396 }
cerionb85e8bb2005-02-16 08:54:33 +00002397 break;
cerion45b70ff2005-01-31 17:03:25 +00002398
sewardjc9659532005-07-21 21:33:57 +00002399 case 0x17:
2400 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion76de5cf2005-11-18 18:25:12 +00002401 DIP("rlwnm%s r%d,r%d,r%d,%d,%d\n", flag_rC ? "." : "",
2402 rA_addr, rS_addr, rB_addr, MaskBegin, MaskEnd);
2403 // rA = ROTL(rS, rB[0-4]) & mask
sewardjc9659532005-07-21 21:33:57 +00002404 // note, ROTL32 does the masking, so we don't do it here
cerion76de5cf2005-11-18 18:25:12 +00002405 assign( rA, binop(Iop_And32, ROTL32(mkexpr(rS), mkexpr(rB)),
sewardjc9659532005-07-21 21:33:57 +00002406 mkU32(mask)) );
2407 break;
cerion45b70ff2005-01-31 17:03:25 +00002408
cerionb85e8bb2005-02-16 08:54:33 +00002409 default:
2410 vex_printf("dis_int_rot(PPC32)(opc1)\n");
2411 return False;
2412 }
cerion645c9302005-01-31 10:09:59 +00002413
cerion76de5cf2005-11-18 18:25:12 +00002414 putIReg( rA_addr, mkexpr(rA) );
2415
2416 if (flag_rC) {
2417 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00002418 }
2419 return True;
cerion645c9302005-01-31 10:09:59 +00002420}
2421
2422
2423
cerion3d870a32005-03-18 12:23:33 +00002424/*
2425 Integer Load Instructions
2426*/
cerion645c9302005-01-31 10:09:59 +00002427static Bool dis_int_load ( UInt theInstr )
2428{
cerion76de5cf2005-11-18 18:25:12 +00002429 /* D-Form, X-Form */
2430 UChar opc1 = ifieldOPC(theInstr);
2431 UChar rD_addr = ifieldRegDS(theInstr);
2432 UChar rA_addr = ifieldRegA(theInstr);
2433 Int d_simm16 = ifieldSIMM16(theInstr);
2434 UChar rB_addr = ifieldRegB(theInstr);
2435 UInt opc2 = ifieldOPClo10(theInstr);
2436 UChar b0 = ifieldBIT0(theInstr);
2437
2438 IRTemp rA_or_0 = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00002439 IRTemp EA_imm = newTemp(Ity_I32);
2440 IRTemp EA_reg = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002441 IRTemp rA = newTemp(Ity_I32);
2442 IRTemp rB = newTemp(Ity_I32);
cerionedf7fc52005-11-18 20:57:41 +00002443
cerion76de5cf2005-11-18 18:25:12 +00002444 assign( rA, getIReg(rA_addr) );
2445 assign( rB, getIReg(rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00002446
cerion76de5cf2005-11-18 18:25:12 +00002447 assign( rA_or_0, ea_rA_or_zero(rA_addr));
cerione9d361a2005-03-04 17:35:29 +00002448
cerion76de5cf2005-11-18 18:25:12 +00002449 assign( EA_imm, binop(Iop_Add32, mkexpr(rA_or_0), mkU32(d_simm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002450
2451 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00002452 case 0x22: // lbz (Load B & Zero, PPC32 p433)
cerion76de5cf2005-11-18 18:25:12 +00002453 DIP("lbz r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2454 putIReg( rD_addr, unop(Iop_8Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002455 loadBE(Ity_I8, mkexpr(EA_imm))) );
2456 break;
2457
cerione9d361a2005-03-04 17:35:29 +00002458 case 0x23: // lbzu (Load B & Zero with Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00002459 if (rA_addr == 0 || rA_addr == rD_addr) {
2460 vex_printf("dis_int_load(PPC32)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002461 return False;
2462 }
cerion76de5cf2005-11-18 18:25:12 +00002463 DIP("lbzu r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2464 putIReg( rD_addr, unop(Iop_8Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002465 loadBE(Ity_I8, mkexpr(EA_imm))) );
cerion76de5cf2005-11-18 18:25:12 +00002466 putIReg( rA_addr, mkexpr(EA_imm) );
cerionb85e8bb2005-02-16 08:54:33 +00002467 break;
2468
cerione9d361a2005-03-04 17:35:29 +00002469 case 0x2A: // lha (Load HW Algebraic, PPC32 p445)
cerion76de5cf2005-11-18 18:25:12 +00002470 DIP("lha r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2471 putIReg( rD_addr, unop(Iop_16Sto32,
cerionb85e8bb2005-02-16 08:54:33 +00002472 loadBE(Ity_I16, mkexpr(EA_imm))) );
2473 break;
cerion645c9302005-01-31 10:09:59 +00002474
cerioncb14e732005-09-09 16:38:19 +00002475 case 0x2B: // lhau (Load HW Algebraic with Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00002476 if (rA_addr == 0 || rA_addr == rD_addr) {
2477 vex_printf("dis_int_load(PPC32)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00002478 return False;
2479 }
cerion76de5cf2005-11-18 18:25:12 +00002480 DIP("lhau r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2481 putIReg( rD_addr, unop(Iop_16Sto32,
cerioncb14e732005-09-09 16:38:19 +00002482 loadBE(Ity_I16, mkexpr(EA_imm))) );
cerion76de5cf2005-11-18 18:25:12 +00002483 putIReg( rA_addr, mkexpr(EA_imm) );
cerioncb14e732005-09-09 16:38:19 +00002484 break;
cerionb85e8bb2005-02-16 08:54:33 +00002485
cerione9d361a2005-03-04 17:35:29 +00002486 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
cerion76de5cf2005-11-18 18:25:12 +00002487 DIP("lhz r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2488 putIReg( rD_addr, unop(Iop_16Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002489 loadBE(Ity_I16, mkexpr(EA_imm))) );
2490 break;
2491
sewardj0e2cc672005-07-29 21:58:51 +00002492 case 0x29: // lhzu (Load HW & and Zero with Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00002493 if (rA_addr == 0 || rA_addr == rD_addr) {
2494 vex_printf("dis_int_load(PPC32)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00002495 return False;
2496 }
cerion76de5cf2005-11-18 18:25:12 +00002497 DIP("lhzu r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2498 putIReg( rD_addr, unop(Iop_16Uto32,
sewardj0e2cc672005-07-29 21:58:51 +00002499 loadBE(Ity_I16, mkexpr(EA_imm))) );
cerion76de5cf2005-11-18 18:25:12 +00002500 putIReg( rA_addr, mkexpr(EA_imm) );
sewardj0e2cc672005-07-29 21:58:51 +00002501 break;
cerion645c9302005-01-31 10:09:59 +00002502
cerione9d361a2005-03-04 17:35:29 +00002503 case 0x20: // lwz (Load W & Zero, PPC32 p460)
cerion76de5cf2005-11-18 18:25:12 +00002504 DIP("lwz r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2505 putIReg( rD_addr, loadBE(Ity_I32, mkexpr(EA_imm)) );
cerionb85e8bb2005-02-16 08:54:33 +00002506 break;
2507
cerione9d361a2005-03-04 17:35:29 +00002508 case 0x21: // lwzu (Load W & Zero with Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00002509 if (rA_addr == 0 || rA_addr == rD_addr) {
2510 vex_printf("dis_int_load(PPC32)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002511 return False;
2512 }
cerion76de5cf2005-11-18 18:25:12 +00002513 DIP("lwzu r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2514 putIReg( rD_addr, loadBE(Ity_I32, mkexpr(EA_imm)) );
2515 putIReg( rA_addr, mkexpr(EA_imm) );
cerionb85e8bb2005-02-16 08:54:33 +00002516 break;
2517
2518 /* X Form */
2519 case 0x1F:
2520 if (b0 != 0) {
2521 vex_printf("dis_int_load(PPC32)(Ox1F,b0)\n");
2522 return False;
2523 }
cerion76de5cf2005-11-18 18:25:12 +00002524 assign( EA_reg, binop(Iop_Add32, mkexpr(rA_or_0), mkexpr(rB)) );
cerion645c9302005-01-31 10:09:59 +00002525
cerionb85e8bb2005-02-16 08:54:33 +00002526 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002527 case 0x077: // lbzux (Load B & Zero with Update Indexed, PPC32 p435)
cerion76de5cf2005-11-18 18:25:12 +00002528 DIP("lbzux r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2529 if (rA_addr == 0 || rA_addr == rD_addr) {
2530 vex_printf("dis_int_load(PPC32)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002531 return False;
2532 }
cerion76de5cf2005-11-18 18:25:12 +00002533 putIReg( rD_addr, unop(Iop_8Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002534 loadBE(Ity_I8, mkexpr(EA_reg))) );
cerion76de5cf2005-11-18 18:25:12 +00002535 putIReg( rA_addr, mkexpr(EA_reg) );
cerionb85e8bb2005-02-16 08:54:33 +00002536 break;
2537
cerione9d361a2005-03-04 17:35:29 +00002538 case 0x057: // lbzx (Load B & Zero Indexed, PPC32 p436)
cerion76de5cf2005-11-18 18:25:12 +00002539 DIP("lbzx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2540 putIReg( rD_addr, unop(Iop_8Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002541 loadBE(Ity_I8, mkexpr(EA_reg))) );
2542 break;
2543
cerioncb14e732005-09-09 16:38:19 +00002544 case 0x177: // lhaux (Load HW Algebraic with Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00002545 if (rA_addr == 0 || rA_addr == rD_addr) {
2546 vex_printf("dis_int_load(PPC32)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00002547 return False;
2548 }
cerion76de5cf2005-11-18 18:25:12 +00002549 DIP("lhaux r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2550 putIReg( rD_addr, unop(Iop_16Sto32,
cerioncb14e732005-09-09 16:38:19 +00002551 loadBE(Ity_I16, mkexpr(EA_reg))) );
cerion76de5cf2005-11-18 18:25:12 +00002552 putIReg( rA_addr, mkexpr(EA_reg) );
cerioncb14e732005-09-09 16:38:19 +00002553 break;
cerionb85e8bb2005-02-16 08:54:33 +00002554
cerione9d361a2005-03-04 17:35:29 +00002555 case 0x157: // lhax (Load HW Algebraic Indexed, PPC32 p448)
cerion76de5cf2005-11-18 18:25:12 +00002556 DIP("lhax r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2557 putIReg( rD_addr, unop(Iop_16Sto32,
cerionb85e8bb2005-02-16 08:54:33 +00002558 loadBE(Ity_I16, mkexpr(EA_reg))) );
2559 break;
2560
cerione9d361a2005-03-04 17:35:29 +00002561 case 0x137: // lhzux (Load HW & Zero with Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00002562 if (rA_addr == 0 || rA_addr == rD_addr) {
2563 vex_printf("dis_int_load(PPC32)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002564 return False;
2565 }
cerion76de5cf2005-11-18 18:25:12 +00002566 DIP("lhzux r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2567 putIReg( rD_addr, unop(Iop_16Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002568 loadBE(Ity_I16, mkexpr(EA_reg))) );
cerion76de5cf2005-11-18 18:25:12 +00002569 putIReg( rA_addr, mkexpr(EA_reg) );
cerionb85e8bb2005-02-16 08:54:33 +00002570 break;
2571
cerione9d361a2005-03-04 17:35:29 +00002572 case 0x117: // lhzx (Load HW & Zero Indexed, PPC32 p453)
cerion76de5cf2005-11-18 18:25:12 +00002573 DIP("lhzx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2574 putIReg( rD_addr, unop(Iop_16Uto32,
cerionb85e8bb2005-02-16 08:54:33 +00002575 loadBE(Ity_I16, mkexpr(EA_reg))) );
2576 break;
cerion44997f22005-01-31 18:45:59 +00002577
sewardj7787af42005-08-04 18:32:19 +00002578 case 0x037: // lwzux (Load W & Zero with Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00002579 if (rA_addr == 0 || rA_addr == rD_addr) {
2580 vex_printf("dis_int_load(PPC32)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00002581 return False;
2582 }
cerion76de5cf2005-11-18 18:25:12 +00002583 DIP("lwzux r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2584 putIReg( rD_addr, loadBE(Ity_I32, mkexpr(EA_reg)) );
2585 putIReg( rA_addr, mkexpr(EA_reg) );
sewardj7787af42005-08-04 18:32:19 +00002586 break;
cerionb85e8bb2005-02-16 08:54:33 +00002587
cerione9d361a2005-03-04 17:35:29 +00002588 case 0x017: // lwzx (Load W & Zero Indexed, PPC32 p463)
cerion76de5cf2005-11-18 18:25:12 +00002589 DIP("lwzx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
2590 putIReg( rD_addr, loadBE(Ity_I32, mkexpr(EA_reg)) );
cerionb85e8bb2005-02-16 08:54:33 +00002591 break;
cerion44997f22005-01-31 18:45:59 +00002592
cerionb85e8bb2005-02-16 08:54:33 +00002593 default:
2594 vex_printf("dis_int_load(PPC32)(opc2)\n");
2595 return False;
2596 }
2597 break;
2598 default:
2599 vex_printf("dis_int_load(PPC32)(opc1)\n");
2600 return False;
2601 }
2602 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00002603}
2604
2605
2606
cerion3d870a32005-03-18 12:23:33 +00002607/*
2608 Integer Store Instructions
2609*/
ceriond23be4e2005-01-31 07:23:07 +00002610static Bool dis_int_store ( UInt theInstr )
2611{
cerion76de5cf2005-11-18 18:25:12 +00002612 /* D-Form, X-Form */
cerionedf7fc52005-11-18 20:57:41 +00002613 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002614 UInt rS_addr = ifieldRegDS(theInstr);
2615 UInt rA_addr = ifieldRegA(theInstr);
2616 Int simm16 = ifieldSIMM16(theInstr);
2617 UInt rB_addr = ifieldRegB(theInstr);
2618 UInt opc2 = ifieldOPClo10(theInstr);
2619 UChar b0 = ifieldBIT0(theInstr);
2620
2621 IRTemp rA_or_0 = newTemp(Ity_I32);
2622 IRTemp rB = newTemp(Ity_I32);
2623 IRTemp rS = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00002624 IRTemp EA_imm = newTemp(Ity_I32);
2625 IRTemp EA_reg = newTemp(Ity_I32);
2626
cerion76de5cf2005-11-18 18:25:12 +00002627 assign( rB, getIReg(rB_addr) );
2628 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00002629
cerion76de5cf2005-11-18 18:25:12 +00002630 assign( rA_or_0, ea_rA_or_zero(rA_addr) );
2631 assign( EA_imm, binop(Iop_Add32, mkexpr(rA_or_0), mkU32(simm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002632
2633 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00002634 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00002635 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2636 storeBE( mkexpr(EA_imm), unop(Iop_32to8, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00002637 break;
sewardjb51f0f42005-07-18 11:38:02 +00002638
cerione9d361a2005-03-04 17:35:29 +00002639 case 0x27: // stbu (Store B with Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00002640 if (rA_addr == 0 ) {
2641 vex_printf("dis_int_store(PPC32)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002642 return False;
2643 }
cerion76de5cf2005-11-18 18:25:12 +00002644 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2645 putIReg( rA_addr, mkexpr(EA_imm) );
2646 storeBE( mkexpr(EA_imm), unop(Iop_32to8, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002647 break;
ceriond23be4e2005-01-31 07:23:07 +00002648
cerione9d361a2005-03-04 17:35:29 +00002649 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00002650 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2651 storeBE( mkexpr(EA_imm), unop(Iop_32to16, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002652 break;
2653
cerione9d361a2005-03-04 17:35:29 +00002654 case 0x2D: // sthu (Store HW with Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00002655 if (rA_addr == 0) {
2656 vex_printf("dis_int_store(PPC32)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002657 return False;
2658 }
cerion76de5cf2005-11-18 18:25:12 +00002659 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2660 putIReg( rA_addr, mkexpr(EA_imm) );
2661 storeBE( mkexpr(EA_imm), unop(Iop_32to16, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002662 break;
ceriond23be4e2005-01-31 07:23:07 +00002663
cerione9d361a2005-03-04 17:35:29 +00002664 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00002665 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2666 storeBE( mkexpr(EA_imm), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00002667 break;
ceriond23be4e2005-01-31 07:23:07 +00002668
cerione9d361a2005-03-04 17:35:29 +00002669 case 0x25: // stwu (Store W with Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00002670 if (rA_addr == 0) {
2671 vex_printf("dis_int_store(PPC32)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002672 return False;
2673 }
cerion76de5cf2005-11-18 18:25:12 +00002674 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
2675 putIReg( rA_addr, mkexpr(EA_imm) );
2676 storeBE( mkexpr(EA_imm), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00002677 break;
2678
2679 /* X Form */
2680 case 0x1F:
2681 if (b0 != 0) {
2682 vex_printf("dis_int_store(PPC32)(0x1F,b0)\n");
2683 return False;
2684 }
cerion76de5cf2005-11-18 18:25:12 +00002685 assign( EA_reg, binop(Iop_Add32, mkexpr(rA_or_0), mkexpr(rB)) );
cerion44997f22005-01-31 18:45:59 +00002686
cerionb85e8bb2005-02-16 08:54:33 +00002687 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002688 case 0x0F7: // stbux (Store B with Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00002689 if (rA_addr == 0) {
2690 vex_printf("dis_int_store(PPC32)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002691 return False;
2692 }
cerion76de5cf2005-11-18 18:25:12 +00002693 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2694 putIReg( rA_addr, mkexpr(EA_reg) );
2695 storeBE( mkexpr(EA_reg), unop(Iop_32to8, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002696 break;
2697
cerione9d361a2005-03-04 17:35:29 +00002698 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00002699 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2700 storeBE( mkexpr(EA_reg), unop(Iop_32to8, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002701 break;
2702
cerioncb14e732005-09-09 16:38:19 +00002703 case 0x1B7: // sthux (Store HW with Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00002704 if (rA_addr == 0) {
2705 vex_printf("dis_int_store(PPC32)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00002706 return False;
2707 }
cerion76de5cf2005-11-18 18:25:12 +00002708 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2709 putIReg( rA_addr, mkexpr(EA_reg) );
2710 storeBE( mkexpr(EA_reg), unop(Iop_32to16, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00002711 break;
cerionb85e8bb2005-02-16 08:54:33 +00002712
cerione9d361a2005-03-04 17:35:29 +00002713 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00002714 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2715 storeBE( mkexpr(EA_reg), unop(Iop_32to16, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00002716 break;
2717
cerione9d361a2005-03-04 17:35:29 +00002718 case 0x0B7: // stwux (Store W with Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00002719 if (rA_addr == 0) {
2720 vex_printf("dis_int_store(PPC32)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002721 return False;
2722 }
cerion76de5cf2005-11-18 18:25:12 +00002723 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2724 putIReg( rA_addr, mkexpr(EA_reg) );
2725 storeBE( mkexpr(EA_reg), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00002726 break;
cerion44997f22005-01-31 18:45:59 +00002727
cerione9d361a2005-03-04 17:35:29 +00002728 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00002729 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
2730 storeBE( mkexpr(EA_reg), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00002731 break;
2732
2733 default:
2734 vex_printf("dis_int_store(PPC32)(opc2)\n");
2735 return False;
2736 }
2737 break;
2738 default:
2739 vex_printf("dis_int_store(PPC32)(opc1)\n");
2740 return False;
2741 }
2742 return True;
ceriond23be4e2005-01-31 07:23:07 +00002743}
2744
2745
2746
sewardj7787af42005-08-04 18:32:19 +00002747/*
2748 Integer Load/Store Multiple Instructions
2749*/
2750static Bool dis_int_ldst_mult ( UInt theInstr )
2751{
2752 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00002753 UChar opc1 = ifieldOPC(theInstr);
2754 UChar rD_addr = ifieldRegDS(theInstr);
2755 UChar rS_addr = rD_addr;
2756 UChar rA_addr = ifieldRegA(theInstr);
2757 Int d_simm16 = ifieldSIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00002758
cerion76de5cf2005-11-18 18:25:12 +00002759 UInt reg_idx = 0;
2760 UInt offset = 0;
2761 IRTemp rA = newTemp(Ity_I32);
2762 IRTemp EA = newTemp(Ity_I32);
sewardj7787af42005-08-04 18:32:19 +00002763 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00002764
cerion76de5cf2005-11-18 18:25:12 +00002765 if (rA_addr == 0) {
2766 assign( EA, binop(Iop_Add32, mkU32(0), mkU32(d_simm16)) );
sewardj7787af42005-08-04 18:32:19 +00002767 } else {
cerion76de5cf2005-11-18 18:25:12 +00002768 assign( rA, getIReg(rA_addr) );
2769 assign( EA, binop(Iop_Add32, mkexpr(rA), mkU32(d_simm16)) );
sewardj7787af42005-08-04 18:32:19 +00002770 }
2771
2772 switch (opc1) {
2773 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
cerion76de5cf2005-11-18 18:25:12 +00002774 if (rA_addr >= rD_addr) {
2775 vex_printf("dis_int_ldst_mult(PPC32)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00002776 return False;
2777 }
cerion76de5cf2005-11-18 18:25:12 +00002778 DIP("lmw r%d,%d(r%d)\n", rD_addr, d_simm16, rA_addr);
2779 for (reg_idx = rD_addr; reg_idx <= 31; reg_idx++) {
sewardj7787af42005-08-04 18:32:19 +00002780 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(offset));
2781 putIReg( reg_idx, loadBE(Ity_I32, irx_addr ) );
2782 offset += 4;
2783 }
2784 break;
2785
2786 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
cerion76de5cf2005-11-18 18:25:12 +00002787 DIP("stmw r%d,%d(r%d)\n", rS_addr, d_simm16, rA_addr);
2788 for (reg_idx = rS_addr; reg_idx <= 31; reg_idx++) {
sewardj7787af42005-08-04 18:32:19 +00002789 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(offset));
2790 storeBE( irx_addr, getIReg(reg_idx) );
2791 offset += 4;
2792 }
2793 break;
2794
2795 default:
2796 vex_printf("dis_int_ldst_mult(PPC32)(opc1)\n");
2797 return False;
2798 }
2799 return True;
2800}
2801
2802
2803
sewardj87e651f2005-09-09 08:31:18 +00002804/*
2805 Integer Load/Store String Instructions
2806*/
2807static
2808void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
2809 IRTemp EA, // EA
2810 Int rD, // first dst register
2811 Int maxBytes, // 32 or 128
2812 Addr32 NIA ) // where next?
2813{
2814 Int i, shift = 24;
2815 IRExpr* e_nbytes = mkexpr(tNBytes);
2816 IRExpr* e_EA = mkexpr(EA);
2817
sewardj5876fa12005-09-09 09:35:29 +00002818 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00002819 rD--; if (rD < 0) rD = 31;
2820
2821 for (i = 0; i < maxBytes; i++) {
2822
2823 /* if (nBytes < (i+1)) goto NIA; */
2824 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
2825 Ijk_Boring,
2826 IRConst_U32(NIA)) );
2827 /* when crossing into a new dest register, set it to zero. */
2828 if ((i % 4) == 0) {
2829 rD++; if (rD == 32) rD = 0;
2830 putIReg(rD, mkU32(0));
2831 shift = 24;
2832 }
2833 /* rD |= (8Uto32(*(EA+i))) << shift */
2834 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
2835 putIReg(
2836 rD,
2837 binop(Iop_Or32,
2838 getIReg(rD),
2839 binop(Iop_Shl32,
2840 unop(Iop_8Uto32,
2841 loadBE(Ity_I8,
2842 binop(Iop_Add32, e_EA, mkU32(i)))),
sewardjc7cd2142005-09-09 22:31:49 +00002843 mkU8(toUChar(shift)))
sewardj87e651f2005-09-09 08:31:18 +00002844 ));
2845 shift -= 8;
2846 }
2847}
2848
sewardj5876fa12005-09-09 09:35:29 +00002849static
2850void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
2851 IRTemp EA, // EA
2852 Int rS, // first src register
2853 Int maxBytes, // 32 or 128
2854 Addr32 NIA ) // where next?
2855{
2856 Int i, shift = 24;
2857 IRExpr* e_nbytes = mkexpr(tNBytes);
2858 IRExpr* e_EA = mkexpr(EA);
2859
2860 vassert(rS >= 0 && rS < 32);
2861 rS--; if (rS < 0) rS = 31;
2862
2863 for (i = 0; i < maxBytes; i++) {
2864 /* if (nBytes < (i+1)) goto NIA; */
2865 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
2866 Ijk_Boring,
2867 IRConst_U32(NIA)) );
2868 /* check for crossing into a new src register. */
2869 if ((i % 4) == 0) {
2870 rS++; if (rS == 32) rS = 0;
2871 shift = 24;
2872 }
2873 /* *(EA+i) = 32to8(rS >> shift) */
2874 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
2875 storeBE(
2876 binop(Iop_Add32, e_EA, mkU32(i)),
2877 unop(Iop_32to8,
sewardjc7cd2142005-09-09 22:31:49 +00002878 binop(Iop_Shr32, getIReg(rS), mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00002879 );
2880 shift -= 8;
2881 }
2882}
2883
sewardj87e651f2005-09-09 08:31:18 +00002884
2885static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
2886{
2887 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00002888 UChar opc1 = ifieldOPC(theInstr);
2889 UChar rD_addr = ifieldRegDS(theInstr);
2890 UChar rS_addr = rD_addr;
2891 UChar rA_addr = ifieldRegA(theInstr);
2892 UChar rB_addr = ifieldRegB(theInstr);
2893 UChar NumBytes = rB_addr;
2894 UInt opc2 = ifieldOPClo10(theInstr);
2895 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00002896
sewardj0f2c5402005-09-09 09:50:34 +00002897 IRTemp t_EA = newTemp(Ity_I32);
sewardj87e651f2005-09-09 08:31:18 +00002898 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00002899
sewardj87e651f2005-09-09 08:31:18 +00002900 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00002901
sewardj87e651f2005-09-09 08:31:18 +00002902 if (opc1 != 0x1F || b0 != 0) {
2903 vex_printf("dis_int_ldst_str(PPC32)(opc1)\n");
2904 return False;
2905 }
2906
2907 switch (opc2) {
2908 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
2909 /* NB: does not reject the case where RA is in the range of
2910 registers to be loaded. It should. */
cerion76de5cf2005-11-18 18:25:12 +00002911 DIP("lswi r%d,r%d,%d\n", rD_addr, rA_addr, NumBytes);
2912 assign( t_EA, ea_rA_or_zero(rA_addr) );
sewardj87e651f2005-09-09 08:31:18 +00002913 if (NumBytes == 8) {
2914 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00002915 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
2916 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00002917 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00002918 putIReg( (rD_addr+1) % 32,
sewardj87e651f2005-09-09 08:31:18 +00002919 loadBE(Ity_I32, binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
2920 } else {
2921 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002922 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
2923 generate_lsw_sequence( t_nbytes, t_EA, rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00002924 32, guest_CIA_curr_instr+4 );
2925 *stopHere = True;
2926 }
2927 return True;
2928
2929 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
2930 /* NB: does not reject the case where RA is in the range of
2931 registers to be loaded. It should. Although considering
2932 that that can only be detected at run time, it's not easy to
2933 do so. */
cerion76de5cf2005-11-18 18:25:12 +00002934 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00002935 return False;
cerion76de5cf2005-11-18 18:25:12 +00002936 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00002937 return False;
cerion76de5cf2005-11-18 18:25:12 +00002938 DIP("lswx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00002939 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002940 assign( t_EA, ea_standard(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00002941 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
cerion76de5cf2005-11-18 18:25:12 +00002942 generate_lsw_sequence( t_nbytes, t_EA, rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00002943 128, guest_CIA_curr_instr+4 );
2944 *stopHere = True;
2945 return True;
2946
sewardj5876fa12005-09-09 09:35:29 +00002947 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
cerion76de5cf2005-11-18 18:25:12 +00002948 DIP("stswi r%d,r%d,%d\n", rS_addr, rA_addr, NumBytes);
2949 assign( t_EA, ea_rA_or_zero(rA_addr) );
sewardj5876fa12005-09-09 09:35:29 +00002950 if (NumBytes == 8) {
2951 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00002952 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
2953 storeBE( mkexpr(t_EA),
2954 getIReg(rD_addr) );
2955 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
2956 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00002957 } else {
2958 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002959 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
2960 generate_stsw_sequence( t_nbytes, t_EA, rD_addr,
sewardj5876fa12005-09-09 09:35:29 +00002961 32, guest_CIA_curr_instr+4 );
2962 *stopHere = True;
2963 }
2964 return True;
2965
sewardj5876fa12005-09-09 09:35:29 +00002966 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
cerion76de5cf2005-11-18 18:25:12 +00002967 DIP("stswx r%d,r%d,r%d\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00002968 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00002969 assign( t_EA, ea_standard(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00002970 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
cerion76de5cf2005-11-18 18:25:12 +00002971 generate_stsw_sequence( t_nbytes, t_EA, rS_addr,
sewardj5876fa12005-09-09 09:35:29 +00002972 128, guest_CIA_curr_instr+4 );
2973 *stopHere = True;
2974 return True;
sewardj87e651f2005-09-09 08:31:18 +00002975
2976 default:
2977 vex_printf("dis_int_ldst_str(PPC32)(opc2)\n");
2978 return False;
2979 }
2980 return True;
2981}
2982
cerion094d1392005-06-20 13:45:57 +00002983
sewardjb51f0f42005-07-18 11:38:02 +00002984/* ------------------------------------------------------------------
2985 Integer Branch Instructions
2986 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00002987
cerion45552a92005-02-03 18:20:22 +00002988/*
2989 Branch helper function
2990 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00002991 Returns an I32 which is 0x00000000 if the ctr condition failed
2992 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00002993*/
sewardjb51f0f42005-07-18 11:38:02 +00002994static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00002995{
sewardjb51f0f42005-07-18 11:38:02 +00002996 IRTemp ok = newTemp(Ity_I32);
cerioned623db2005-06-20 12:42:04 +00002997
cerionb85e8bb2005-02-16 08:54:33 +00002998 if ((BO >> 2) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00002999 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00003000 } else {
cerionb85e8bb2005-02-16 08:54:33 +00003001 if ((BO >> 1) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00003002 assign( ok, unop( Iop_1Sto32,
3003 binop( Iop_CmpEQ32,
3004 getSPR( PPC32_SPR_CTR ), mkU32(0))) );
cerionb85e8bb2005-02-16 08:54:33 +00003005 } else {
sewardjb51f0f42005-07-18 11:38:02 +00003006 assign( ok, unop( Iop_1Sto32,
3007 binop( Iop_CmpNE32,
3008 getSPR( PPC32_SPR_CTR ), mkU32(0))) );
cerionb85e8bb2005-02-16 08:54:33 +00003009 }
3010 }
3011 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00003012}
3013
sewardjb51f0f42005-07-18 11:38:02 +00003014
cerion45552a92005-02-03 18:20:22 +00003015/*
sewardjb51f0f42005-07-18 11:38:02 +00003016 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
3017 Returns an I32 which is either 0 if the condition failed or
3018 some arbitrary nonzero value otherwise. */
3019
3020static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00003021{
sewardjb51f0f42005-07-18 11:38:02 +00003022 Int where;
3023 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00003024 IRTemp cr_bi = newTemp(Ity_I32);
3025
sewardjb51f0f42005-07-18 11:38:02 +00003026 if ((BO >> 4) & 1) {
3027 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00003028 } else {
sewardjb51f0f42005-07-18 11:38:02 +00003029 // ok = (CR[BI] == BO[3]) Note, the following relies on
3030 // getCRbit_anywhere returning a value which
3031 // is either zero or has exactly 1 bit set.
3032 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00003033
3034 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00003035 /* We can use cr_bi as-is. */
3036 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00003037 } else {
sewardjb51f0f42005-07-18 11:38:02 +00003038 /* We have to invert the sense of the information held in
3039 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00003040 getCRbit_anywhere regards as significant. */
sewardjb51f0f42005-07-18 11:38:02 +00003041 assign( res, binop(Iop_Xor32, mkexpr(cr_bi), mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00003042 }
3043 }
sewardjb51f0f42005-07-18 11:38:02 +00003044 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00003045}
3046
3047
cerion3d870a32005-03-18 12:23:33 +00003048/*
3049 Integer Branch Instructions
3050*/
sewardj9d540e52005-10-08 11:28:16 +00003051static Bool dis_branch ( UInt theInstr,
3052 /*OUT*/DisResult* dres,
3053 Bool (*resteerOkFn)(Addr64) )
cerion91ad5362005-01-27 23:02:41 +00003054{
cerion76de5cf2005-11-18 18:25:12 +00003055 UChar opc1 = ifieldOPC(theInstr);
3056 UChar BO = ifieldRegDS(theInstr);
3057 UChar BI = ifieldRegA(theInstr);
3058 Int BD_s16 = ifieldSIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
3059 UChar b11to15 = ifieldRegB(theInstr);
3060 UInt opc2 = ifieldOPClo10(theInstr);
3061 Int LI_s26 = ifieldSIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
3062 UChar flag_AA = ifieldBIT1(theInstr);
3063 UChar flag_LK = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00003064
3065 Addr32 nia = 0;
3066
cerionb85e8bb2005-02-16 08:54:33 +00003067 IRTemp ir_nia = newTemp(Ity_I32);
3068 IRTemp do_branch = newTemp(Ity_I32);
sewardjb51f0f42005-07-18 11:38:02 +00003069 IRTemp ctr_ok = newTemp(Ity_I32);
3070 IRTemp cond_ok = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00003071
cerionb85e8bb2005-02-16 08:54:33 +00003072 /* Hack to pass through code that just wants to read the PC */
3073 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00003074 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
3075 putSPR( PPC32_SPR_LR, mkU32(guest_CIA_curr_instr + 4) );
cerionb85e8bb2005-02-16 08:54:33 +00003076 return True;
sewardjb51f0f42005-07-18 11:38:02 +00003077 }
sewardj9d540e52005-10-08 11:28:16 +00003078
3079 /* The default what-next. Individual cases can override it. */
3080 dres->whatNext = Dis_StopHere;
3081
cerionb85e8bb2005-02-16 08:54:33 +00003082 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003083 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00003084 if (flag_AA) {
cerion76de5cf2005-11-18 18:25:12 +00003085 nia = (UInt)LI_s26;
cerion4561acb2005-02-21 14:07:48 +00003086 } else {
cerion76de5cf2005-11-18 18:25:12 +00003087 nia = (UInt)((Int)guest_CIA_curr_instr + LI_s26);
cerionb85e8bb2005-02-16 08:54:33 +00003088 }
cerione9d361a2005-03-04 17:35:29 +00003089 DIP("b%s%s 0x%x\n", flag_LK ? "l" : "", flag_AA ? "a" : "", nia);
3090
cerionb85e8bb2005-02-16 08:54:33 +00003091 if (flag_LK) {
sewardjb51f0f42005-07-18 11:38:02 +00003092 putSPR( PPC32_SPR_LR, mkU32(guest_CIA_curr_instr + 4) );
sewardj9d540e52005-10-08 11:28:16 +00003093 }
3094
3095 if (resteerOkFn((Addr64)nia)) {
3096 dres->whatNext = Dis_Resteer;
3097 dres->continueAt = (Addr64)nia;
3098 } else {
3099 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
3100 irbb->next = mkU32(nia);
3101 }
cerionb85e8bb2005-02-16 08:54:33 +00003102 break;
3103
cerione9d361a2005-03-04 17:35:29 +00003104 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00003105 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
cerion76de5cf2005-11-18 18:25:12 +00003106 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD_s16);
cerionb85e8bb2005-02-16 08:54:33 +00003107
3108 if (!(BO & 0x4)) {
sewardjb51f0f42005-07-18 11:38:02 +00003109 putSPR( PPC32_SPR_CTR,
3110 binop(Iop_Sub32, getSPR( PPC32_SPR_CTR ), mkU32(1)) );
cerionb85e8bb2005-02-16 08:54:33 +00003111 }
sewardjb51f0f42005-07-18 11:38:02 +00003112
3113 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00003114 cond_ok is either zero or nonzero, since that's the cheapest
3115 way to compute it. Anding them together gives a value which
3116 is either zero or non zero and so that's what we must test
3117 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00003118 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00003119 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00003120 assign( do_branch,
3121 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
3122
cerion4561acb2005-02-21 14:07:48 +00003123 if (flag_AA) {
cerion76de5cf2005-11-18 18:25:12 +00003124 nia = (UInt)BD_s16;
cerion4561acb2005-02-21 14:07:48 +00003125 } else {
cerion76de5cf2005-11-18 18:25:12 +00003126 nia = (UInt)((Int)guest_CIA_curr_instr + BD_s16);
cerionb85e8bb2005-02-16 08:54:33 +00003127 }
cerionb85e8bb2005-02-16 08:54:33 +00003128 if (flag_LK) {
sewardjb51f0f42005-07-18 11:38:02 +00003129 putSPR( PPC32_SPR_LR, mkU32(guest_CIA_curr_instr + 4) );
cerionb85e8bb2005-02-16 08:54:33 +00003130 }
3131
sewardjb51f0f42005-07-18 11:38:02 +00003132 stmt( IRStmt_Exit( binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
cerionb85e8bb2005-02-16 08:54:33 +00003133 flag_LK ? Ijk_Call : Ijk_Boring,
3134 IRConst_U32(nia) ));
3135
3136 irbb->jumpkind = Ijk_Boring;
sewardj9e6491a2005-07-02 19:24:10 +00003137 irbb->next = mkU32(guest_CIA_curr_instr + 4);
cerionb85e8bb2005-02-16 08:54:33 +00003138 break;
3139
3140 case 0x13:
3141 if (b11to15!=0) {
3142 vex_printf("dis_int_branch(PPC32)(0x13,b11to15)\n");
3143 return False;
3144 }
cerion91ad5362005-01-27 23:02:41 +00003145
cerionb85e8bb2005-02-16 08:54:33 +00003146 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00003147 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerionb85e8bb2005-02-16 08:54:33 +00003148 if ((BO & 0x4) == 0) { // "decrement and test CTR" option invalid
3149 vex_printf("dis_int_branch(PPC32)(bcctr,BO)\n");
3150 return False;
3151 }
ceriona31e8b52005-02-21 16:30:45 +00003152 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00003153
3154 assign( cond_ok, branch_cond_ok( BO, BI ) );
3155
sewardjb51f0f42005-07-18 11:38:02 +00003156 assign( ir_nia,
3157 binop(Iop_And32, mkU32(0xFFFFFFFC),
3158 getSPR( PPC32_SPR_CTR ) ));
cerionb85e8bb2005-02-16 08:54:33 +00003159
3160 if (flag_LK) {
sewardjb51f0f42005-07-18 11:38:02 +00003161 putSPR( PPC32_SPR_LR, mkU32(guest_CIA_curr_instr + 4) );
cerionb85e8bb2005-02-16 08:54:33 +00003162 }
3163
sewardjb51f0f42005-07-18 11:38:02 +00003164 stmt( IRStmt_Exit(
3165 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
3166 Ijk_Boring,
3167 IRConst_U32(guest_CIA_curr_instr + 4)
3168 ));
cerionb85e8bb2005-02-16 08:54:33 +00003169
3170 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
3171 irbb->next = mkexpr(ir_nia);
3172 break;
3173
cerione9d361a2005-03-04 17:35:29 +00003174 case 0x010: // bclr (Branch Cond. to Link Register, PPC32 p365)
sewardjb51f0f42005-07-18 11:38:02 +00003175
3176 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00003177 DIP("blr\n");
sewardjb51f0f42005-07-18 11:38:02 +00003178 } else {
3179 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
3180 }
cerion91ad5362005-01-27 23:02:41 +00003181
cerionb85e8bb2005-02-16 08:54:33 +00003182 if (!(BO & 0x4)) {
sewardjb51f0f42005-07-18 11:38:02 +00003183 putSPR( PPC32_SPR_CTR,
3184 binop(Iop_Sub32, getSPR( PPC32_SPR_CTR ), mkU32(1)) );
cerionb85e8bb2005-02-16 08:54:33 +00003185 }
3186
sewardjb51f0f42005-07-18 11:38:02 +00003187 /* See comments above for 'bc' about this */
3188 assign( ctr_ok, branch_ctr_ok( BO ) );
3189 assign( cond_ok, branch_cond_ok( BO, BI ) );
3190 assign( do_branch,
3191 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerionb85e8bb2005-02-16 08:54:33 +00003192
3193 assign( ir_nia, binop(Iop_And32,
sewardjb51f0f42005-07-18 11:38:02 +00003194 getSPR( PPC32_SPR_LR ),
cerionb85e8bb2005-02-16 08:54:33 +00003195 mkU32(0xFFFFFFFC)) );
3196 if (flag_LK) {
sewardjb51f0f42005-07-18 11:38:02 +00003197 putSPR( PPC32_SPR_LR, mkU32(guest_CIA_curr_instr + 4) );
cerionb85e8bb2005-02-16 08:54:33 +00003198 }
sewardjb51f0f42005-07-18 11:38:02 +00003199
3200 stmt( IRStmt_Exit(
3201 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
3202 Ijk_Boring,
3203 IRConst_U32(guest_CIA_curr_instr + 4)
3204 ));
3205
sewardjd37be032005-11-12 12:56:31 +00003206 /* blrl is pretty strange; it's like a return that sets the
3207 return address of its caller to the insn following this
3208 one. Mark it as a return. */
3209 irbb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
cerionb85e8bb2005-02-16 08:54:33 +00003210 irbb->next = mkexpr(ir_nia);
3211 break;
3212
3213 default:
3214 vex_printf("dis_int_branch(PPC32)(opc2)\n");
3215 return False;
3216 }
3217 break;
sewardj9d540e52005-10-08 11:28:16 +00003218
cerionb85e8bb2005-02-16 08:54:33 +00003219 default:
3220 vex_printf("dis_int_branch(PPC32)(opc1)\n");
3221 return False;
3222 }
cerion91ad5362005-01-27 23:02:41 +00003223
cerionb85e8bb2005-02-16 08:54:33 +00003224 return True;
cerion91ad5362005-01-27 23:02:41 +00003225}
3226
3227
3228
cerion3d870a32005-03-18 12:23:33 +00003229/*
3230 Condition Register Logical Instructions
3231*/
cerion3007c7f2005-02-23 23:13:29 +00003232static Bool dis_cond_logic ( UInt theInstr )
3233{
3234 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00003235 UChar opc1 = ifieldOPC(theInstr);
3236 UChar crbD_addr = ifieldRegDS(theInstr);
3237 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
3238 UChar crbA_addr = ifieldRegA(theInstr);
3239 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
3240 UChar crbB_addr = ifieldRegB(theInstr);
3241 UInt opc2 = ifieldOPClo10(theInstr);
3242 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00003243
3244 IRTemp crbD = newTemp(Ity_I32);
3245 IRTemp crbA = newTemp(Ity_I32);
3246 IRTemp crbB = newTemp(Ity_I32);
3247
3248 if (opc1 != 19 || b0 != 0) {
3249 vex_printf("dis_cond_logic(PPC32)(opc1)\n");
3250 return False;
3251 }
3252
cerione9d361a2005-03-04 17:35:29 +00003253 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00003254 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00003255 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
3256 vex_printf("dis_cond_logic(PPC32)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00003257 return False;
cerion76de5cf2005-11-18 18:25:12 +00003258 }
sewardjb51f0f42005-07-18 11:38:02 +00003259 DIP("mcrf cr%d,cr%d\n", crfD_addr, crfS_addr);
3260 putCR0( crfD_addr, getCR0( crfS_addr) );
3261 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00003262 } else {
sewardjb51f0f42005-07-18 11:38:02 +00003263 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00003264 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00003265 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00003266 else
sewardjb51f0f42005-07-18 11:38:02 +00003267 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00003268
3269 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00003270 case 0x101: // crand (Cond Reg AND, PPC32 p372)
3271 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3272 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
3273 break;
sewardj7787af42005-08-04 18:32:19 +00003274 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
3275 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3276 assign( crbD, binop(Iop_And32,
3277 mkexpr(crbA),
3278 unop(Iop_Not32, mkexpr(crbB))) );
3279 break;
sewardje14bb9f2005-07-22 09:39:02 +00003280 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
3281 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3282 assign( crbD, unop(Iop_Not32,
3283 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
3284 break;
sewardj7c2dc712005-09-08 17:33:27 +00003285 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
3286 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3287 assign( crbD, unop(Iop_Not32,
3288 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
3289 break;
cerione9d361a2005-03-04 17:35:29 +00003290 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00003291 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3292 assign( crbD, unop(Iop_Not32,
3293 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
3294 break;
cerione9d361a2005-03-04 17:35:29 +00003295 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00003296 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3297 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
3298 break;
sewardj7c2dc712005-09-08 17:33:27 +00003299 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
3300 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3301 assign( crbD, binop(Iop_Or32,
3302 mkexpr(crbA),
3303 unop(Iop_Not32, mkexpr(crbB))) );
3304 break;
cerione9d361a2005-03-04 17:35:29 +00003305 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00003306 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
3307 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
3308 break;
cerion3007c7f2005-02-23 23:13:29 +00003309 default:
3310 vex_printf("dis_cond_logic(PPC32)(opc2)\n");
3311 return False;
3312 }
3313
sewardjb51f0f42005-07-18 11:38:02 +00003314 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00003315 }
3316 return True;
3317}
3318
3319
cerion3d870a32005-03-18 12:23:33 +00003320/*
3321 System Linkage Instructions
3322*/
sewardj9e6491a2005-07-02 19:24:10 +00003323static Bool dis_syslink ( UInt theInstr, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00003324{
cerionb85e8bb2005-02-16 08:54:33 +00003325 if (theInstr != 0x44000002) {
3326 vex_printf("dis_int_syslink(PPC32)(theInstr)\n");
3327 return False;
3328 }
cerione1d857b2005-02-04 18:29:05 +00003329
cerione9d361a2005-03-04 17:35:29 +00003330 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00003331 DIP("sc\n");
3332
3333 /* It's important that all ArchRegs carry their up-to-date value
3334 at this point. So we declare an end-of-block here, which
3335 forces any TempRegs caching ArchRegs to be flushed. */
sewardj9e6491a2005-07-02 19:24:10 +00003336 irbb->next = mkU32( guest_CIA_curr_instr + 4 );
sewardj4fa325a2005-11-03 13:27:24 +00003337 irbb->jumpkind = Ijk_Sys_syscall;
cerionb85e8bb2005-02-16 08:54:33 +00003338
sewardj9e6491a2005-07-02 19:24:10 +00003339 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00003340 return True;
cerion8c3adda2005-01-31 11:54:05 +00003341}
3342
cerion3d870a32005-03-18 12:23:33 +00003343
3344/*
3345 Memory Synchronization Instructions
3346*/
cerion8c3adda2005-01-31 11:54:05 +00003347static Bool dis_memsync ( UInt theInstr )
3348{
cerionb85e8bb2005-02-16 08:54:33 +00003349 /* X-Form, XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00003350 UChar opc1 = ifieldOPC(theInstr);
3351 UInt b11to25 = IFIELD(theInstr, 11, 15);
3352 UChar rD_addr = ifieldRegDS(theInstr);
3353 UChar rS_addr = rD_addr;
3354 UChar rA_addr = ifieldRegA(theInstr);
3355 UChar rB_addr = ifieldRegB(theInstr);
3356 UInt opc2 = ifieldOPClo10(theInstr);
3357 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003358
cerionb85e8bb2005-02-16 08:54:33 +00003359 IRTemp EA = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00003360 IRTemp rS = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00003361
3362 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003363 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00003364 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00003365 if (opc2 != 0x096) {
3366 vex_printf("dis_int_memsync(PPC32)(0x13,opc2)\n");
3367 return False;
3368 }
3369 if (b11to25 != 0 || b0 != 0) {
3370 vex_printf("dis_int_memsync(PPC32)(0x13,b11to25|b0)\n");
3371 return False;
3372 }
3373 DIP("isync\n");
cerionb85e8bb2005-02-16 08:54:33 +00003374 stmt( IRStmt_MFence() );
3375 break;
cerion8c3adda2005-01-31 11:54:05 +00003376
cerionb85e8bb2005-02-16 08:54:33 +00003377 /* X-Form */
3378 case 0x1F:
3379 switch (opc2) {
sewardj7787af42005-08-04 18:32:19 +00003380 case 0x356: // eieio (Enforce In-Order Execution of I/O, PPC32 p394)
3381 if (b11to25 != 0 || b0 != 0) {
3382 vex_printf("dis_int_memsync(PPC32)(eiei0,b11to25|b0)\n");
3383 return False;
3384 }
3385 DIP("eieio\n");
3386 /* Insert a memory fence, just to be on the safe side. */
3387 stmt( IRStmt_MFence() );
3388 break;
cerion8c3adda2005-01-31 11:54:05 +00003389
cerione9d361a2005-03-04 17:35:29 +00003390 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
cerionb85e8bb2005-02-16 08:54:33 +00003391 if (b0 != 0) {
3392 vex_printf("dis_int_memsync(PPC32)(lwarx,b0)\n");
3393 return False;
3394 }
cerion76de5cf2005-11-18 18:25:12 +00003395 DIP("lwarx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
3396 assign( EA, ea_standard(rA_addr, rB_addr) );
3397 putIReg( rD_addr, loadBE(Ity_I32, mkexpr(EA)) );
3398 /* Take a reservation */
sewardj7787af42005-08-04 18:32:19 +00003399 stmt( IRStmt_Put( OFFB_RESVN, mkexpr(EA) ));
cerionb85e8bb2005-02-16 08:54:33 +00003400 break;
3401
sewardj7787af42005-08-04 18:32:19 +00003402 case 0x096: {
3403 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
sewardj7787af42005-08-04 18:32:19 +00003404 IRTemp resaddr = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00003405 if (b0 != 1) {
3406 vex_printf("dis_int_memsync(PPC32)(stwcx.,b0)\n");
3407 return False;
3408 }
cerion76de5cf2005-11-18 18:25:12 +00003409 DIP("stwcx. r%d,r%d,r%d\n", rS_addr, rA_addr, rB_addr);
3410 assign( rS, getIReg(rS_addr) );
3411 assign( EA, ea_standard(rA_addr, rB_addr) );
sewardjafe85832005-09-09 10:25:39 +00003412
cerion76de5cf2005-11-18 18:25:12 +00003413 /* First set up as if the reservation failed */
sewardj7787af42005-08-04 18:32:19 +00003414 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
3415 putCR321(0, mkU8(0<<1));
3416 putCR0(0, getXER_SO());
3417
cerion76de5cf2005-11-18 18:25:12 +00003418 /* Get the reservation address into a temporary, then
3419 clear it. */
3420 assign( resaddr, IRExpr_Get(OFFB_RESVN, Ity_I32) );
sewardj7787af42005-08-04 18:32:19 +00003421 stmt( IRStmt_Put( OFFB_RESVN, mkU32(0) ));
3422
cerion76de5cf2005-11-18 18:25:12 +00003423 /* Skip the rest if the reservation really did fail. */
sewardj7787af42005-08-04 18:32:19 +00003424 stmt( IRStmt_Exit(
3425 binop(Iop_CmpNE32, mkexpr(resaddr),
3426 mkexpr(EA)),
sewardj7787af42005-08-04 18:32:19 +00003427 Ijk_Boring,
cerionedf7fc52005-11-18 20:57:41 +00003428 IRConst_U32(guest_CIA_curr_instr + 4)) );
sewardj7787af42005-08-04 18:32:19 +00003429
cerion76de5cf2005-11-18 18:25:12 +00003430 /* Success? Do the store */
3431 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00003432
sewardjb51f0f42005-07-18 11:38:02 +00003433 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
3434 putCR321(0, mkU8(1<<1));
cerionb85e8bb2005-02-16 08:54:33 +00003435 break;
sewardj7787af42005-08-04 18:32:19 +00003436 }
3437
cerione9d361a2005-03-04 17:35:29 +00003438 case 0x256: // sync (Synchronize, PPC32 p543)
cerionb85e8bb2005-02-16 08:54:33 +00003439 if (b11to25 != 0 || b0 != 0) {
3440 vex_printf("dis_int_memsync(PPC32)(sync,b11to25|b0)\n");
3441 return False;
3442 }
3443 DIP("sync\n");
3444 /* Insert a memory fence. It's sometimes important that these
3445 are carried through to the generated code. */
3446 stmt( IRStmt_MFence() );
3447 break;
3448
3449 default:
3450 vex_printf("dis_int_memsync(PPC32)(opc2)\n");
3451 return False;
3452 }
3453 break;
cerion8c3adda2005-01-31 11:54:05 +00003454
cerionb85e8bb2005-02-16 08:54:33 +00003455 default:
3456 vex_printf("dis_int_memsync(PPC32)(opc1)\n");
3457 return False;
3458 }
3459 return True;
cerion8c3adda2005-01-31 11:54:05 +00003460}
3461
3462
3463
cerion3d870a32005-03-18 12:23:33 +00003464/*
3465 Integer Shift Instructions
3466*/
cerion645c9302005-01-31 10:09:59 +00003467static Bool dis_int_shift ( UInt theInstr )
3468{
cerionb85e8bb2005-02-16 08:54:33 +00003469 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00003470 UChar opc1 = ifieldOPC(theInstr);
3471 UChar rS_addr = ifieldRegDS(theInstr);
3472 UChar rA_addr = ifieldRegA(theInstr);
3473 UChar rB_addr = ifieldRegB(theInstr);
3474 UChar sh_imm = rB_addr;
3475 UInt opc2 = ifieldOPClo10(theInstr);
3476 UChar flag_rC = ifieldBIT0(theInstr);
3477
cerionb85e8bb2005-02-16 08:54:33 +00003478 IRTemp sh_amt = newTemp(Ity_I8);
cerion76de5cf2005-11-18 18:25:12 +00003479 IRTemp rS = newTemp(Ity_I32);
3480 IRTemp rA = newTemp(Ity_I32);
3481 IRTemp rB = newTemp(Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00003482 IRTemp sh_amt32 = newTemp(Ity_I32);
3483 IRTemp outofrange = newTemp(Ity_I8);
cerionb85e8bb2005-02-16 08:54:33 +00003484
cerion76de5cf2005-11-18 18:25:12 +00003485 assign( rS, getIReg(rS_addr) );
3486 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003487
3488 if (opc1 == 0x1F) {
3489 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00003490 case 0x018: // slw (Shift Left Word, PPC32 p505)
cerion76de5cf2005-11-18 18:25:12 +00003491 DIP("slw%s r%d,r%d,r%d\n", flag_rC ? "." : "",
3492 rA_addr, rS_addr, rB_addr);
3493 /* rA = rS << rB */
3494 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00003495 slw(x,y) = (x << (y & 31)) -- primary result
3496 & ~((y << 26) >>s 31) -- make result 0
3497 for y in 32 .. 63
3498 */
cerionedf7fc52005-11-18 20:57:41 +00003499 assign( rA,
sewardjdfb11442005-10-08 19:58:48 +00003500 binop(
3501 Iop_And32,
3502 binop( Iop_Shl32,
cerion76de5cf2005-11-18 18:25:12 +00003503 mkexpr(rS),
sewardjdfb11442005-10-08 19:58:48 +00003504 unop( Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00003505 binop(Iop_And32, mkexpr(rB), mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00003506 unop( Iop_Not32,
3507 binop( Iop_Sar32,
cerion76de5cf2005-11-18 18:25:12 +00003508 binop(Iop_Shl32, mkexpr(rB), mkU8(26)),
cerionedf7fc52005-11-18 20:57:41 +00003509 mkU8(31)))) );
cerionb85e8bb2005-02-16 08:54:33 +00003510 break;
3511
cerione9d361a2005-03-04 17:35:29 +00003512 case 0x318: // sraw (Shift Right Algebraic Word, PPC32 p506)
cerion76de5cf2005-11-18 18:25:12 +00003513 DIP("sraw%s r%d,r%d,r%d\n", flag_rC ? "." : "",
3514 rA_addr, rS_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00003515
cerion76de5cf2005-11-18 18:25:12 +00003516 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
3517 amt = rB & 63
3518 rA = Sar32( rS, amt > 31 ? 31 : amt )
3519 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
3520 */
3521 assign( sh_amt32, binop(Iop_And32, mkU32(0x3F), mkexpr(rB)) );
3522 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00003523 unop( Iop_1Uto8,
3524 binop(Iop_CmpLT32U, mkU32(31), mkexpr(sh_amt32)) ));
cerion76de5cf2005-11-18 18:25:12 +00003525 assign( rA,
sewardj20ef5472005-07-21 14:48:31 +00003526 binop( Iop_Sar32,
cerion76de5cf2005-11-18 18:25:12 +00003527 mkexpr(rS),
sewardj20ef5472005-07-21 14:48:31 +00003528 unop( Iop_32to8,
3529 IRExpr_Mux0X( mkexpr(outofrange),
3530 mkexpr(sh_amt32),
3531 mkU32(31)) ))
3532 );
3533 set_XER_CA( PPC32G_FLAG_OP_SRAW,
cerion76de5cf2005-11-18 18:25:12 +00003534 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt32),
cerionedf7fc52005-11-18 20:57:41 +00003535 getXER_CA32() );
cerionb85e8bb2005-02-16 08:54:33 +00003536 break;
3537
cerione9d361a2005-03-04 17:35:29 +00003538 case 0x338: // srawi (Shift Right Algebraic Word Immediate, PPC32 p507)
cerion76de5cf2005-11-18 18:25:12 +00003539 DIP("srawi%s r%d,r%d,%d\n", flag_rC ? "." : "",
3540 rA_addr, rS_addr, sh_imm);
3541 vassert(sh_imm < 32);
cerionb85e8bb2005-02-16 08:54:33 +00003542 assign( sh_amt, mkU8(sh_imm) );
cerion76de5cf2005-11-18 18:25:12 +00003543 assign( rA, binop(Iop_Sar32, mkexpr(rS), mkexpr(sh_amt)) );
sewardj20ef5472005-07-21 14:48:31 +00003544 set_XER_CA( PPC32G_FLAG_OP_SRAWI,
cerion76de5cf2005-11-18 18:25:12 +00003545 mkexpr(rA), mkexpr(rS), mkU32(sh_imm),
cerionedf7fc52005-11-18 20:57:41 +00003546 getXER_CA32() );
cerionb85e8bb2005-02-16 08:54:33 +00003547 break;
3548
cerione9d361a2005-03-04 17:35:29 +00003549 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion76de5cf2005-11-18 18:25:12 +00003550 DIP("srw%s r%d,r%d,r%d\n", flag_rC ? "." : "",
3551 rA_addr, rS_addr, rB_addr);
3552 /* rA = rS >>u rB */
3553 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00003554 slw(x,y) = (x >>u (y & 31)) -- primary result
3555 & ~((y << 26) >>s 31) -- make result 0
3556 for y in 32 .. 63
3557 */
cerionedf7fc52005-11-18 20:57:41 +00003558 assign( rA,
sewardjdfb11442005-10-08 19:58:48 +00003559 binop(
3560 Iop_And32,
3561 binop( Iop_Shr32,
cerion76de5cf2005-11-18 18:25:12 +00003562 mkexpr(rS),
sewardjdfb11442005-10-08 19:58:48 +00003563 unop( Iop_32to8,
cerion76de5cf2005-11-18 18:25:12 +00003564 binop(Iop_And32, mkexpr(rB), mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00003565 unop( Iop_Not32,
3566 binop( Iop_Sar32,
cerion76de5cf2005-11-18 18:25:12 +00003567 binop(Iop_Shl32, mkexpr(rB), mkU8(26)),
cerionedf7fc52005-11-18 20:57:41 +00003568 mkU8(31)))) );
cerionb85e8bb2005-02-16 08:54:33 +00003569 break;
3570
3571 default:
3572 vex_printf("dis_int_shift(PPC32)(opc2)\n");
3573 return False;
3574 }
3575 } else {
3576 vex_printf("dis_int_shift(PPC32)(opc1)\n");
3577 return False;
3578 }
cerion0d330c52005-02-28 16:43:16 +00003579
cerion76de5cf2005-11-18 18:25:12 +00003580 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003581
cerion76de5cf2005-11-18 18:25:12 +00003582 if (flag_rC) {
3583 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003584 }
3585 return True;
cerion645c9302005-01-31 10:09:59 +00003586}
3587
3588
3589
sewardj602857d2005-09-06 09:10:09 +00003590/*
3591 Integer Load/Store Reverse Instructions
3592*/
sewardjfb957972005-09-08 17:53:03 +00003593static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
3594{
3595 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
3596 return
3597 binop(Iop_Or32,
3598 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
3599 binop(Iop_Or32,
3600 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
3601 mkU32(0x00FF0000)),
3602 binop(Iop_Or32,
3603 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
3604 mkU32(0x0000FF00)),
3605 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
3606 mkU32(0x000000FF) )
3607 )));
3608}
3609
sewardj602857d2005-09-06 09:10:09 +00003610static Bool dis_int_ldst_rev ( UInt theInstr )
3611{
3612 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00003613 UChar opc1 = ifieldOPC(theInstr);
3614 UChar rD_addr = ifieldRegDS(theInstr);
3615 UChar rS_addr = rD_addr;
3616 UChar rA_addr = ifieldRegA(theInstr);
3617 UChar rB_addr = ifieldRegB(theInstr);
3618 UInt opc2 = ifieldOPClo10(theInstr);
3619 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003620
3621 IRTemp EA = newTemp(Ity_I32);
3622 IRTemp w1 = newTemp(Ity_I32);
3623 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00003624
3625 if (opc1 != 0x1F || b0 != 0) {
3626 vex_printf("dis_int_ldst_rev(PPC32)(opc1|b0)\n");
3627 return False;
3628 }
sewardjafe85832005-09-09 10:25:39 +00003629
cerion76de5cf2005-11-18 18:25:12 +00003630 assign( EA, ea_standard(rA_addr, rB_addr) );
sewardj602857d2005-09-06 09:10:09 +00003631
3632 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00003633//zz case 0x316: // lhbrx (Load Half Word Byte-Reverse Indexed, PPC32 p449)
3634//zz vassert(0);
3635//zz
cerion76de5cf2005-11-18 18:25:12 +00003636//zz DIP("lhbrx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00003637//zz assign( byte0, loadBE(Ity_I8, mkexpr(EA)) );
3638//zz assign( byte1, loadBE(Ity_I8, binop(Iop_Add32, mkexpr(EA),mkU32(1))) );
cerion76de5cf2005-11-18 18:25:12 +00003639//zz assign( rD, binop(Iop_Or32,
sewardjb51f0f42005-07-18 11:38:02 +00003640//zz binop(Iop_Shl32, mkexpr(byte1), mkU8(8)),
3641//zz mkexpr(byte0)) );
cerion76de5cf2005-11-18 18:25:12 +00003642//zz putIReg( rD_addr, mkexpr(rD));
sewardjb51f0f42005-07-18 11:38:02 +00003643//zz break;
sewardj602857d2005-09-06 09:10:09 +00003644
sewardjfb957972005-09-08 17:53:03 +00003645 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
cerion76de5cf2005-11-18 18:25:12 +00003646 DIP("lwbrx r%d,r%d,r%d\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00003647 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
3648 assign( w2, gen_byterev32(w1) );
cerion76de5cf2005-11-18 18:25:12 +00003649 putIReg( rD_addr, mkexpr(w2));
sewardjfb957972005-09-08 17:53:03 +00003650 break;
sewardj602857d2005-09-06 09:10:09 +00003651
sewardjb51f0f42005-07-18 11:38:02 +00003652//zz case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
3653//zz vassert(0);
3654//zz
cerion76de5cf2005-11-18 18:25:12 +00003655//zz DIP("sthbrx r%d,r%d,r%d\n", rS_addr, rA_addr, rB_addr);
3656//zz assign( rS, getIReg(rS_addr) );
3657//zz assign( byte0, binop(Iop_And32, mkexpr(rS), mkU32(0x00FF)) );
3658//zz assign( byte1, binop(Iop_And32, mkexpr(rS), mkU32(0xFF00)) );
sewardjb51f0f42005-07-18 11:38:02 +00003659//zz
3660//zz assign( tmp16,
3661//zz unop(Iop_32to16,
3662//zz binop(Iop_Or32,
3663//zz binop(Iop_Shl32, mkexpr(byte0), mkU8(8)),
3664//zz binop(Iop_Shr32, mkexpr(byte1), mkU8(8)))) );
3665//zz storeBE( mkexpr(EA), getIReg(tmp16) );
3666//zz break;
sewardj602857d2005-09-06 09:10:09 +00003667
sewardjfb957972005-09-08 17:53:03 +00003668 case 0x296: // stwbrx (Store Word Byte-Reverse Indexed, PPC32 p531)
cerion76de5cf2005-11-18 18:25:12 +00003669 DIP("stwbrx r%d,r%d,r%d\n", rS_addr, rA_addr, rB_addr);
3670 assign( w1, getIReg(rS_addr) );
sewardjfb957972005-09-08 17:53:03 +00003671 storeBE( mkexpr(EA), gen_byterev32(w1) );
3672 break;
3673
3674 default:
3675 vex_printf("dis_int_ldst_rev(PPC32)(opc2)\n");
3676 return False;
sewardj602857d2005-09-06 09:10:09 +00003677 }
3678 return True;
3679}
cerion645c9302005-01-31 10:09:59 +00003680
3681
3682
cerion3d870a32005-03-18 12:23:33 +00003683/*
3684 Processor Control Instructions
3685*/
cerion8c3adda2005-01-31 11:54:05 +00003686static Bool dis_proc_ctl ( UInt theInstr )
3687{
cerion76de5cf2005-11-18 18:25:12 +00003688 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00003689
3690 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00003691 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
3692 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
3693 UChar rD_addr = ifieldRegDS(theInstr);
3694 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00003695
cerion76de5cf2005-11-18 18:25:12 +00003696 /* XFX-Form */
3697 UChar rS_addr = rD_addr;
3698 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00003699 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00003700 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
3701 UInt CRM = IFIELD( theInstr, 12, 8 );
3702 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
3703
3704 UInt opc2 = ifieldOPClo10(theInstr);
3705 UChar b0 = ifieldBIT0(theInstr);
3706
sewardj41a7b702005-11-18 22:18:23 +00003707 IRTemp rS;
3708
cerion76de5cf2005-11-18 18:25:12 +00003709 /* Reorder SPR field as per PPC32 p470 */
3710 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
cerion48090c02005-02-24 11:19:51 +00003711
sewardj73a91972005-09-06 10:25:46 +00003712 /* Reorder TBR field as per PPC32 p475 */
3713 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
3714
sewardj41a7b702005-11-18 22:18:23 +00003715 rS = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00003716 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003717
3718 if (opc1 != 0x1F || b0 != 0) {
3719 vex_printf("dis_proc_ctl(PPC32)(opc1|b0)\n");
3720 return False;
3721 }
3722
3723 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00003724 /* X-Form */
sewardj55ccc3e2005-09-09 19:45:02 +00003725 case 0x200: { // mcrxr (Move to Condition Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00003726 if (b21to22 != 0 || b11to20 != 0) {
3727 vex_printf("dis_proc_ctl(PPC32)(mcrxr,b21to22|b11to20)\n");
3728 return False;
3729 }
3730 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00003731 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
3732 putSPR_field( PPC32_SPR_CR,
3733 getSPR_field( PPC32_SPR_XER, 7 ),
3734 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00003735
3736 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00003737 putXER_SO( mkU8(0) );
3738 putXER_OV( mkU8(0) );
3739 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00003740 break;
sewardj55ccc3e2005-09-09 19:45:02 +00003741 }
cerionb85e8bb2005-02-16 08:54:33 +00003742
cerione9d361a2005-03-04 17:35:29 +00003743 case 0x013: // mfcr (Move from Condition Register, PPC32 p467)
cerionb85e8bb2005-02-16 08:54:33 +00003744 if (b11to20 != 0) {
3745 vex_printf("dis_proc_ctl(PPC32)(mfcr,b11to20)\n");
3746 return False;
3747 }
cerion76de5cf2005-11-18 18:25:12 +00003748 DIP("mfcr r%d\n", rD_addr);
cerionedf7fc52005-11-18 20:57:41 +00003749 putIReg( rD_addr, getSPR( PPC32_SPR_CR ) );
cerionb85e8bb2005-02-16 08:54:33 +00003750 break;
3751
3752 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00003753 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00003754
cerion76de5cf2005-11-18 18:25:12 +00003755 switch (SPR) { // Choose a register...
sewardj20ef5472005-07-21 14:48:31 +00003756
3757 case 0x1:
cerion76de5cf2005-11-18 18:25:12 +00003758 DIP("mfxer r%d\n", rD_addr);
cerionedf7fc52005-11-18 20:57:41 +00003759 putIReg( rD_addr, getSPR( PPC32_SPR_XER ) );
sewardj20ef5472005-07-21 14:48:31 +00003760 break;
sewardjb51f0f42005-07-18 11:38:02 +00003761 case 0x8:
cerion76de5cf2005-11-18 18:25:12 +00003762 DIP("mflr r%d\n", rD_addr);
3763 putIReg( rD_addr, getSPR( PPC32_SPR_LR ) );
sewardjb51f0f42005-07-18 11:38:02 +00003764 break;
3765 case 0x9:
cerion76de5cf2005-11-18 18:25:12 +00003766 DIP("mfctr r%d\n", rD_addr);
3767 putIReg( rD_addr, getSPR( PPC32_SPR_CTR ) );
sewardjb51f0f42005-07-18 11:38:02 +00003768 break;
3769 case 0x100:
cerion76de5cf2005-11-18 18:25:12 +00003770 DIP("mfvrsave r%d\n", rD_addr);
3771 putIReg( rD_addr, getSPR( PPC32_SPR_VRSAVE ) );
sewardjb51f0f42005-07-18 11:38:02 +00003772 break;
ceriona982c052005-06-28 17:23:09 +00003773
sewardjb51f0f42005-07-18 11:38:02 +00003774 case 0x012: case 0x013: case 0x016:
3775 case 0x019: case 0x01A: case 0x01B:
3776 case 0x110: case 0x111: case 0x112: case 0x113:
3777 // case 0x118: // 64bit only
3778 case 0x11A: case 0x11F:
3779 case 0x210: case 0x211: case 0x212: case 0x213:
3780 case 0x214: case 0x215: case 0x216: case 0x217:
3781 case 0x218: case 0x219: case 0x21A: case 0x21B:
3782 case 0x21C: case 0x21D: case 0x21E: case 0x21F:
3783 case 0x3F5:
3784 vex_printf("dis_proc_ctl(PPC32)(mfspr) - supervisor level op\n");
3785 return False;
cerion45552a92005-02-03 18:20:22 +00003786
sewardjb51f0f42005-07-18 11:38:02 +00003787 default:
cerionedf7fc52005-11-18 20:57:41 +00003788 vex_printf("dis_proc_ctl(PPC32)(mfspr,SPR)(0x%x)\n", SPR);
sewardjb51f0f42005-07-18 11:38:02 +00003789 return False;
cerionb85e8bb2005-02-16 08:54:33 +00003790 }
3791 break;
3792
sewardj73a91972005-09-06 10:25:46 +00003793 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
3794 IRTemp val = newTemp(Ity_I64);
3795 IRExpr** args = mkIRExprVec_0();
3796 IRDirty* d = unsafeIRDirty_1_N (
3797 val,
3798 0/*regparms*/,
3799 "ppc32g_dirtyhelper_MFTB",
3800 &ppc32g_dirtyhelper_MFTB,
3801 args
3802 );
3803 /* execute the dirty call, dumping the result in val. */
3804 stmt( IRStmt_Dirty(d) );
3805
3806 switch (TBR) {
3807 case 269:
cerion76de5cf2005-11-18 18:25:12 +00003808 putIReg( rD_addr, unop(Iop_64HIto32, mkexpr(val)) );
3809 DIP("mftbu r%d", rD_addr);
sewardj73a91972005-09-06 10:25:46 +00003810 break;
3811 case 268:
cerion76de5cf2005-11-18 18:25:12 +00003812 putIReg( rD_addr, unop(Iop_64to32, mkexpr(val)) );
3813 DIP("mftb r%d", rD_addr);
sewardj73a91972005-09-06 10:25:46 +00003814 break;
3815 default:
3816 return False; /* illegal instruction */
3817 }
3818 break;
3819 }
3820
cerionedf7fc52005-11-18 20:57:41 +00003821 case 0x090: { // mtcrf (Move to Condition Register Fields, PPC32 p477)
3822 Int cr;
3823 UChar shft;
cerionb85e8bb2005-02-16 08:54:33 +00003824 if (b11 != 0 || b20 != 0) {
3825 vex_printf("dis_proc_ctl(PPC32)(mtcrf,b11|b20)\n");
3826 return False;
3827 }
cerion76de5cf2005-11-18 18:25:12 +00003828 DIP("mtcrf 0x%x,r%d\n", CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00003829 /* Write to each field specified by CRM */
3830 for (cr = 0; cr < 8; cr++) {
3831 if ((CRM & (1 << (7-cr))) == 0)
3832 continue;
3833 shft = 4*(7-cr);
3834 putSPR_field( PPC32_SPR_CR,
3835 binop(Iop_Shr32, mkexpr(rS), mkU8(shft)), cr );
3836 }
cerionb85e8bb2005-02-16 08:54:33 +00003837 break;
cerionedf7fc52005-11-18 20:57:41 +00003838 }
cerione9d361a2005-03-04 17:35:29 +00003839
3840 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00003841
cerion76de5cf2005-11-18 18:25:12 +00003842 switch (SPR) { // Choose a register...
sewardj20ef5472005-07-21 14:48:31 +00003843 case 0x1:
cerion76de5cf2005-11-18 18:25:12 +00003844 DIP("mtxer r%d\n", rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00003845 putSPR( PPC32_SPR_XER, mkexpr(rS) );
sewardj20ef5472005-07-21 14:48:31 +00003846 break;
sewardjb51f0f42005-07-18 11:38:02 +00003847 case 0x8:
cerion76de5cf2005-11-18 18:25:12 +00003848 DIP("mtlr r%d\n", rS_addr);
3849 putSPR( PPC32_SPR_LR, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003850 break;
3851 case 0x9:
cerion76de5cf2005-11-18 18:25:12 +00003852 DIP("mtctr r%d\n", rS_addr);
3853 putSPR( PPC32_SPR_CTR, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003854 break;
3855 case 0x100:
cerion76de5cf2005-11-18 18:25:12 +00003856 DIP("mtvrsave r%d\n", rS_addr);
3857 putSPR( PPC32_SPR_VRSAVE, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003858 break;
cerion8c3adda2005-01-31 11:54:05 +00003859
sewardjb51f0f42005-07-18 11:38:02 +00003860 default:
cerionedf7fc52005-11-18 20:57:41 +00003861 vex_printf("dis_proc_ctl(PPC32)(mtspr,SPR)(%u)\n", SPR);
sewardjb51f0f42005-07-18 11:38:02 +00003862 return False;
cerionb85e8bb2005-02-16 08:54:33 +00003863 }
3864 break;
3865
3866 default:
3867 vex_printf("dis_proc_ctl(PPC32)(opc2)\n");
3868 return False;
3869 }
3870 return True;
cerion8c3adda2005-01-31 11:54:05 +00003871}
3872
3873
cerion3d870a32005-03-18 12:23:33 +00003874/*
3875 Cache Management Instructions
3876*/
sewardjd94b73a2005-06-30 12:08:48 +00003877static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00003878 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00003879 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00003880{
cerionb85e8bb2005-02-16 08:54:33 +00003881 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00003882 UChar opc1 = ifieldOPC(theInstr);
3883 UChar b21to25 = ifieldRegDS(theInstr);
3884 UChar rA_addr = ifieldRegA(theInstr);
3885 UChar rB_addr = ifieldRegB(theInstr);
3886 UInt opc2 = ifieldOPClo10(theInstr);
3887 UChar b0 = ifieldBIT0(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00003888 Int lineszB = guest_archinfo->ppc32_cache_line_szB;
cerion094d1392005-06-20 13:45:57 +00003889
cerionb85e8bb2005-02-16 08:54:33 +00003890 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
3891 vex_printf("dis_cache_manage(PPC32)(opc1|b21to25|b0)\n");
3892 return False;
3893 }
sewardjd94b73a2005-06-30 12:08:48 +00003894
3895 /* stay sane .. */
3896 vassert(lineszB == 32 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00003897
3898 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00003899//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
3900//zz vassert(0); /* AWAITING TEST CASE */
cerion76de5cf2005-11-18 18:25:12 +00003901//zz DIP("dcba r%d,r%d\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00003902//zz if (0) vex_printf("vex ppc32->IR: kludged dcba\n");
3903//zz break;
sewardj20ef5472005-07-21 14:48:31 +00003904
3905 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
cerion76de5cf2005-11-18 18:25:12 +00003906 DIP("dcbf r%d,r%d\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00003907 /* nop as far as vex is concerned */
3908 if (0) vex_printf("vex ppc32->IR: kludged dcbf\n");
3909 break;
cerionb85e8bb2005-02-16 08:54:33 +00003910
cerione9d361a2005-03-04 17:35:29 +00003911 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
cerion76de5cf2005-11-18 18:25:12 +00003912 DIP("dcbst r%d,r%d\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00003913 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00003914 break;
cerion8c3adda2005-01-31 11:54:05 +00003915
cerione9d361a2005-03-04 17:35:29 +00003916 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
cerion76de5cf2005-11-18 18:25:12 +00003917 DIP("dcbt r%d,r%d\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00003918 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00003919 break;
3920
cerione9d361a2005-03-04 17:35:29 +00003921 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
cerion76de5cf2005-11-18 18:25:12 +00003922 DIP("dcbtst r%d,r%d\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00003923 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00003924 break;
3925
cerion094d1392005-06-20 13:45:57 +00003926 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00003927 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion094d1392005-06-20 13:45:57 +00003928 IRTemp EA = newTemp(Ity_I32);
3929 IRTemp addr = newTemp(Ity_I32);
3930 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00003931 UInt i;
cerion76de5cf2005-11-18 18:25:12 +00003932 DIP("dcbz r%d,r%d\n", rA_addr, rB_addr);
cerionedf7fc52005-11-18 20:57:41 +00003933 assign( EA, binop( Iop_Add32,
3934 getIReg(rB_addr),
3935 rA_addr==0 ? mkU32(0) : getIReg(rA_addr)) );
cerion094d1392005-06-20 13:45:57 +00003936
3937 /* Round EA down to the start of the containing block. */
cerionedf7fc52005-11-18 20:57:41 +00003938 assign( addr, binop( Iop_And32,
3939 mkexpr(EA),
3940 mkU32( ~(lineszB-1) )) );
cerion094d1392005-06-20 13:45:57 +00003941
sewardjd94b73a2005-06-30 12:08:48 +00003942 for (i = 0; i < lineszB / 4; i++) {
3943 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
3944 storeBE( irx_addr, mkU32(0) );
cerion094d1392005-06-20 13:45:57 +00003945 }
cerionb85e8bb2005-02-16 08:54:33 +00003946 break;
cerion094d1392005-06-20 13:45:57 +00003947 }
cerion8c3adda2005-01-31 11:54:05 +00003948
sewardj7ce9d152005-03-15 16:54:13 +00003949 case 0x3D6: {
3950 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
3951 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00003952 block at (rA|0) + rB. */
sewardj7ce9d152005-03-15 16:54:13 +00003953 IRTemp addr = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00003954 DIP("icbi r%d,r%d\n", rA_addr, rB_addr);
sewardj7ce9d152005-03-15 16:54:13 +00003955
3956 assign( addr,
3957 binop( Iop_Add32,
cerion76de5cf2005-11-18 18:25:12 +00003958 getIReg(rB_addr),
3959 rA_addr==0 ? mkU32(0) : getIReg(rA_addr)) );
sewardj7ce9d152005-03-15 16:54:13 +00003960
3961 /* Round addr down to the start of the containing block. */
3962 stmt( IRStmt_Put(
3963 OFFB_TISTART,
3964 binop( Iop_And32,
3965 mkexpr(addr),
sewardjd94b73a2005-06-30 12:08:48 +00003966 mkU32( ~(lineszB-1) ))) );
sewardj7ce9d152005-03-15 16:54:13 +00003967
sewardjd94b73a2005-06-30 12:08:48 +00003968 stmt( IRStmt_Put(OFFB_TILEN, mkU32(lineszB) ) );
sewardj7ce9d152005-03-15 16:54:13 +00003969
sewardja8078f62005-03-15 18:27:40 +00003970 /* be paranoid ... */
3971 stmt( IRStmt_MFence() );
3972
sewardj7ce9d152005-03-15 16:54:13 +00003973 irbb->jumpkind = Ijk_TInval;
sewardj9e6491a2005-07-02 19:24:10 +00003974 irbb->next = mkU32(guest_CIA_curr_instr + 4);
3975 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00003976 break;
sewardj7ce9d152005-03-15 16:54:13 +00003977 }
cerion8c3adda2005-01-31 11:54:05 +00003978
cerionb85e8bb2005-02-16 08:54:33 +00003979 default:
3980 vex_printf("dis_cache_manage(PPC32)(opc2)\n");
3981 return False;
3982 }
3983 return True;
cerion8c3adda2005-01-31 11:54:05 +00003984}
3985
3986
sewardje14bb9f2005-07-22 09:39:02 +00003987/*------------------------------------------------------------*/
3988/*--- Floating Point Helpers ---*/
3989/*------------------------------------------------------------*/
3990
3991/* --- Set the emulation-warning pseudo-register. --- */
3992
3993static void put_emwarn ( IRExpr* e /* :: Ity_I32 */ )
3994{
3995 vassert(typeOfIRExpr(irbb->tyenv,e) == Ity_I32);
3996 stmt( IRStmt_Put( OFFB_EMWARN, e ) );
3997}
3998
3999/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
4000/* Produces a value in 0 .. 3, which is encoded as per the type
4001 IRRoundingMode. PPC32RoundingMode encoding is different to
4002 IRRoundingMode, so need to map it.
4003*/
4004static IRExpr* /* :: Ity_I32 */ get_roundingmode ( void )
4005{
4006/*
4007 rounding mode | PPC | IR
4008 ------------------------
4009 to nearest | 00 | 00
4010 to zero | 01 | 11
4011 to +infinity | 10 | 10
4012 to -infinity | 11 | 01
4013*/
4014 IRTemp rm_PPC32 = newTemp(Ity_I32);
cerionedf7fc52005-11-18 20:57:41 +00004015 assign( rm_PPC32, getSPR_masked( PPC32_SPR_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00004016
4017 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
4018 return binop(Iop_Xor32, mkexpr(rm_PPC32),
4019 binop(Iop_And32, mkU32(2),
4020 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1))));
4021}
4022
4023/* Round float to single precision
4024 - returns type Ity_F64 */
4025static IRExpr* roundToSgl ( IRExpr* src )
4026{
4027 return unop(Iop_F32toF64, binop(Iop_F64toF32, get_roundingmode(), src));
4028}
cerion094d1392005-06-20 13:45:57 +00004029
4030
cerion3d870a32005-03-18 12:23:33 +00004031/*------------------------------------------------------------*/
4032/*--- Floating Point Instruction Translation ---*/
4033/*------------------------------------------------------------*/
4034
4035/*
4036 Floating Point Load Instructions
4037*/
4038static Bool dis_fp_load ( UInt theInstr )
4039{
cerion76de5cf2005-11-18 18:25:12 +00004040 /* X-Form, D-Form */
4041 UChar opc1 = ifieldOPC(theInstr);
4042 UChar frD_addr = ifieldRegDS(theInstr);
4043 UChar rA_addr = ifieldRegA(theInstr);
4044 UChar rB_addr = ifieldRegB(theInstr);
4045 UInt opc2 = ifieldOPClo10(theInstr);
4046 UChar b0 = ifieldBIT0(theInstr);
4047 Int d_simm16 = ifieldSIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00004048
4049 IRTemp EA = newTemp(Ity_I32);
4050 IRTemp rA = newTemp(Ity_I32);
4051 IRTemp rB = newTemp(Ity_I32);
4052 IRTemp rA_or_0 = newTemp(Ity_I32);
4053
4054 assign( rA, getIReg(rA_addr) );
4055 assign( rB, getIReg(rB_addr) );
sewardjafe85832005-09-09 10:25:39 +00004056 assign( rA_or_0, ea_rA_or_zero(rA_addr) );
cerion3d870a32005-03-18 12:23:33 +00004057
4058 switch(opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00004059 case 0x30: // lfs (Load Float Single, PPC32 p441)
cerion76de5cf2005-11-18 18:25:12 +00004060 DIP("lfs fr%d,%d(r%d)\n", frD_addr, d_simm16, rA_addr);
4061 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA_or_0)) );
sewardje14bb9f2005-07-22 09:39:02 +00004062 putFReg( frD_addr, unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
4063 break;
4064
sewardjb51f0f42005-07-18 11:38:02 +00004065//zz case 0x31: // lfsu (Load Float Single with Update, PPC32 p442)
4066//zz if (rA_addr == 0) {
4067//zz vex_printf("dis_fp_load(PPC32)(instr,lfsu)\n");
4068//zz return False;
4069//zz }
cerion76de5cf2005-11-18 18:25:12 +00004070//zz DIP("lfsu fr%d,%d(r%d)\n", frD_addr, d_simm16, rA_addr);
4071//zz assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA)) );
sewardjb51f0f42005-07-18 11:38:02 +00004072//zz putFReg( frD_addr, unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
4073//zz putIReg( rA_addr, mkexpr(EA) );
4074//zz break;
cerion094d1392005-06-20 13:45:57 +00004075
4076 case 0x32: // lfd (Load Float Double, PPC32 p437)
cerion76de5cf2005-11-18 18:25:12 +00004077 DIP("lfd fr%d,%d(r%d)\n", frD_addr, d_simm16, rA_addr);
4078 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA_or_0)) );
cerion094d1392005-06-20 13:45:57 +00004079 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
4080 break;
cerion3d870a32005-03-18 12:23:33 +00004081
sewardj0e2cc672005-07-29 21:58:51 +00004082 case 0x33: // lfdu (Load Float Double with Update, PPC32 p438)
4083 if (rA_addr == 0) {
4084 vex_printf("dis_fp_load(PPC32)(instr,lfdu)\n");
4085 return False;
4086 }
cerion76de5cf2005-11-18 18:25:12 +00004087 DIP("lfdu fr%d,%d(r%d)\n", frD_addr, d_simm16, rA_addr);
4088 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA)) );
sewardj0e2cc672005-07-29 21:58:51 +00004089 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
4090 putIReg( rA_addr, mkexpr(EA) );
4091 break;
sewardje14bb9f2005-07-22 09:39:02 +00004092
4093 case 0x1F:
4094 if (b0 != 0) {
4095 vex_printf("dis_fp_load(PPC32)(instr,b0)\n");
4096 return False;
4097 }
4098
4099 switch(opc2) {
4100 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
4101 DIP("lfsx fr%d,r%d,r%d\n", frD_addr, rA_addr, rB_addr);
4102 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA_or_0)) );
4103 putFReg( frD_addr, unop( Iop_F32toF64,
4104 loadBE(Ity_F32, mkexpr(EA))) );
4105 break;
4106
sewardj0e2cc672005-07-29 21:58:51 +00004107 case 0x237: // lfsux (Load Float Single with Update Indexed, PPC32 p443)
4108 if (rA_addr == 0) {
4109 vex_printf("dis_fp_load(PPC32)(instr,lfsux)\n");
4110 return False;
4111 }
4112 DIP("lfsux fr%d,r%d,r%d\n", frD_addr, rA_addr, rB_addr);
4113 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA)) );
4114 putFReg( frD_addr, unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
4115 putIReg( rA_addr, mkexpr(EA) );
4116 break;
sewardje14bb9f2005-07-22 09:39:02 +00004117
4118 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
4119 DIP("lfdx fr%d,r%d,r%d\n", frD_addr, rA_addr, rB_addr);
4120 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA_or_0)) );
4121 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
4122 break;
4123
sewardj0e2cc672005-07-29 21:58:51 +00004124 case 0x277: // lfdux (Load Float Double with Update Indexed, PPC32 p439)
4125 if (rA_addr == 0) {
4126 vex_printf("dis_fp_load(PPC32)(instr,lfdux)\n");
4127 return False;
4128 }
4129 DIP("lfdux fr%d,r%d,r%d\n", frD_addr, rA_addr, rB_addr);
4130 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA)) );
4131 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
4132 putIReg( rA_addr, mkexpr(EA) );
4133 break;
sewardje14bb9f2005-07-22 09:39:02 +00004134
4135 default:
4136 vex_printf("dis_fp_load(PPC32)(opc2)\n");
4137 return False;
4138 }
4139 break;
cerion3d870a32005-03-18 12:23:33 +00004140
4141 default:
4142 vex_printf("dis_fp_load(PPC32)(opc1)\n");
4143 return False;
4144 }
4145 return True;
4146}
4147
4148
4149
4150/*
4151 Floating Point Store Instructions
4152*/
4153static Bool dis_fp_store ( UInt theInstr )
4154{
cerion76de5cf2005-11-18 18:25:12 +00004155 /* X-Form, D-Form */
4156 UChar opc1 = ifieldOPC(theInstr);
4157 UChar frS_addr = ifieldRegDS(theInstr);
4158 UChar rA_addr = ifieldRegA(theInstr);
4159 UChar rB_addr = ifieldRegB(theInstr);
4160 UInt opc2 = ifieldOPClo10(theInstr);
4161 UChar b0 = ifieldBIT0(theInstr);
4162 Int d_simm16 = ifieldSIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00004163
4164 IRTemp EA = newTemp(Ity_I32);
4165 IRTemp frS = newTemp(Ity_F64);
4166 IRTemp rA = newTemp(Ity_I32);
4167 IRTemp rB = newTemp(Ity_I32);
4168 IRTemp rA_or_0 = newTemp(Ity_I32);
4169
4170 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004171 assign( rA, getIReg(rA_addr) );
4172 assign( rB, getIReg(rB_addr) );
sewardjafe85832005-09-09 10:25:39 +00004173 assign( rA_or_0, ea_rA_or_zero(rA_addr) );
cerion3d870a32005-03-18 12:23:33 +00004174
4175 switch(opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00004176
4177 case 0x34: // stfs (Store Float Single, PPC32 p518)
cerion76de5cf2005-11-18 18:25:12 +00004178 DIP("stfs fr%d,%d(r%d)\n", frS_addr, d_simm16, rA_addr);
4179 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA_or_0)) );
sewardje14bb9f2005-07-22 09:39:02 +00004180 storeBE( mkexpr(EA),
4181 binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
4182 break;
4183
sewardjb51f0f42005-07-18 11:38:02 +00004184//zz case 0x35: // stfsu (Store Float Single with Update, PPC32 p519)
4185//zz if (rA_addr == 0) {
4186//zz vex_printf("dis_fp_store(PPC32)(instr,stfsu)\n");
4187//zz return False;
4188//zz }
cerion76de5cf2005-11-18 18:25:12 +00004189//zz DIP("stfsu fr%d,%d(r%d)\n", frS_addr, d_simm16, rA_addr);
4190//zz assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA)) );
sewardjb51f0f42005-07-18 11:38:02 +00004191//zz storeBE( mkexpr(EA),
4192//zz binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
4193//zz putIReg( rA_addr, mkexpr(EA) );
4194//zz break;
cerion3d870a32005-03-18 12:23:33 +00004195
cerion094d1392005-06-20 13:45:57 +00004196 case 0x36: // stfd (Store Float Double, PPC32 p513)
cerion76de5cf2005-11-18 18:25:12 +00004197 DIP("stfd fr%d,%d(r%d)\n", frS_addr, d_simm16, rA_addr);
4198 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA_or_0)) );
cerion094d1392005-06-20 13:45:57 +00004199 storeBE( mkexpr(EA), mkexpr(frS) );
4200 break;
cerion3d870a32005-03-18 12:23:33 +00004201
sewardje14bb9f2005-07-22 09:39:02 +00004202 case 0x37: // stfdu (Store Float Double with Update, PPC32 p514)
4203 if (rA_addr == 0) {
4204 vex_printf("dis_fp_store(PPC32)(instr,stfdu)\n");
4205 return False;
4206 }
cerion76de5cf2005-11-18 18:25:12 +00004207 DIP("stfdu fr%d,%d(r%d)\n", frS_addr, d_simm16, rA_addr);
4208 assign( EA, binop(Iop_Add32, mkU32(d_simm16), mkexpr(rA)) );
sewardje14bb9f2005-07-22 09:39:02 +00004209 storeBE( mkexpr(EA), mkexpr(frS) );
4210 putIReg( rA_addr, mkexpr(EA) );
4211 break;
4212
4213 case 0x1F:
4214 if (b0 != 0) {
4215 vex_printf("dis_fp_store(PPC32)(instr,b0)\n");
4216 return False;
4217 }
4218
4219 switch(opc2) {
4220 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
4221 DIP("stfsx fr%d,r%d,r%d\n", frS_addr, rA_addr, rB_addr);
4222 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA_or_0)) );
4223 storeBE( mkexpr(EA),
4224 binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
4225 break;
4226
sewardjb51f0f42005-07-18 11:38:02 +00004227//zz case 0x2B7: // stfsux (Store Float Single with Update Indexed, PPC32 p520)
4228//zz if (rA_addr == 0) {
4229//zz vex_printf("dis_fp_store(PPC32)(instr,stfsux)\n");
4230//zz return False;
4231//zz }
4232//zz DIP("stfsux fr%d,r%d,r%d\n", frS_addr, rA_addr, rB_addr);
4233//zz assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA)) );
4234//zz storeBE( mkexpr(EA),
4235//zz binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
4236//zz putIReg( rA_addr, mkexpr(EA) );
4237//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00004238
4239 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
4240 DIP("stfdx fr%d,r%d,r%d\n", frS_addr, rA_addr, rB_addr);
4241 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA_or_0)) );
4242 storeBE( mkexpr(EA), mkexpr(frS) );
4243 break;
4244
sewardj5f63c0c2005-09-09 10:36:55 +00004245 case 0x2F7: // stfdux (Store Float Double with Update Indexed, PPC32 p515)
4246 if (rA_addr == 0) {
4247 vex_printf("dis_fp_store(PPC32)(instr,stfdux)\n");
4248 return False;
4249 }
4250 DIP("stfdux fr%d,r%d,r%d\n", frS_addr, rA_addr, rB_addr);
4251 assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA)) );
4252 storeBE( mkexpr(EA), mkexpr(frS) );
4253 putIReg( rA_addr, mkexpr(EA) );
4254 break;
4255
sewardjb51f0f42005-07-18 11:38:02 +00004256//zz case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
4257//zz DIP("stfiwx fr%d,r%d,r%d\n", frS_addr, rA_addr, rB_addr);
4258//zz assign( EA, binop(Iop_Add32, mkexpr(rB), mkexpr(rA_or_0)) );
4259//zz storeBE( mkexpr(EA),
4260//zz unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
4261//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00004262
4263 default:
4264 vex_printf("dis_fp_store(PPC32)(opc2)\n");
4265 return False;
4266 }
4267 break;
cerion3d870a32005-03-18 12:23:33 +00004268
4269 default:
4270 vex_printf("dis_fp_store(PPC32)(opc1)\n");
4271 return False;
4272 }
4273 return True;
4274}
4275
4276
4277
4278/*
4279 Floating Point Arith Instructions
4280*/
4281static Bool dis_fp_arith ( UInt theInstr )
4282{
4283 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00004284 UChar opc1 = ifieldOPC(theInstr);
4285 UChar frD_addr = ifieldRegDS(theInstr);
4286 UChar frA_addr = ifieldRegA(theInstr);
4287 UChar frB_addr = ifieldRegB(theInstr);
4288 UChar frC_addr = ifieldRegC(theInstr);
4289 UChar opc2 = ifieldOPClo5(theInstr);
4290 UChar flag_rC = ifieldBIT0(theInstr);
4291 // Note: flag_rC ignored as fp exceptions not supported.
cerion094d1392005-06-20 13:45:57 +00004292
4293 IRTemp frD = newTemp(Ity_F64);
4294 IRTemp frA = newTemp(Ity_F64);
4295 IRTemp frB = newTemp(Ity_F64);
4296 IRTemp frC = newTemp(Ity_F64);
4297
4298 assign( frA, getFReg(frA_addr));
4299 assign( frB, getFReg(frB_addr));
4300 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00004301
4302 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00004303 case 0x3B:
4304 switch (opc2) {
4305 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
4306 if (frC_addr != 0) {
4307 vex_printf("dis_fp_arith(PPC32)(instr,fdivs)\n");
4308 return False;
4309 }
cerion76de5cf2005-11-18 18:25:12 +00004310 DIP("fdivs%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004311 frD_addr, frA_addr, frB_addr);
4312 assign( frD, roundToSgl( binop(Iop_DivF64, mkexpr(frA), mkexpr(frB)) ));
4313 break;
4314
4315 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
4316 if (frC_addr != 0) {
4317 vex_printf("dis_fp_arith(PPC32)(instr,fsubs)\n");
4318 return False;
4319 }
cerion76de5cf2005-11-18 18:25:12 +00004320 DIP("fsubs%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004321 frD_addr, frA_addr, frB_addr);
4322 assign( frD, roundToSgl(
4323 binop(Iop_SubF64, mkexpr(frA), mkexpr(frB)) ));
4324 break;
4325
4326 case 0x15: // fadds (Floating Add Single, PPC32 p401)
4327 if (frC_addr != 0) {
4328 vex_printf("dis_fp_arith(PPC32)(instr,fadds)\n");
4329 return False;
4330 }
cerion76de5cf2005-11-18 18:25:12 +00004331 DIP("fadds%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004332 frD_addr, frA_addr, frB_addr);
4333 assign( frD, roundToSgl(
4334 binop(Iop_AddF64, mkexpr(frA), mkexpr(frB)) ));
4335 break;
4336
sewardjb51f0f42005-07-18 11:38:02 +00004337//zz case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
4338//zz if (frA_addr != 0 || frC_addr != 0) {
4339//zz vex_printf("dis_fp_arith(PPC32)(instr,fsqrts)\n");
4340//zz return False;
4341//zz }
cerion76de5cf2005-11-18 18:25:12 +00004342//zz DIP("fsqrts%s fr%d,fr%d\n", flag_rC ? "." : "",
sewardjb51f0f42005-07-18 11:38:02 +00004343//zz frD_addr, frB_addr);
4344//zz assign( frD, roundToSgl( unop(Iop_SqrtF64, mkexpr(frB)) ));
4345//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00004346
sewardjb51f0f42005-07-18 11:38:02 +00004347//zz case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
4348//zz if (frA_addr != 0 || frC_addr != 0) {
4349//zz vex_printf("dis_fp_arith(PPC32)(instr,fres)\n");
4350//zz return False;
4351//zz }
cerion76de5cf2005-11-18 18:25:12 +00004352//zz DIP("fres%s fr%d,fr%d\n", flag_rC ? "." : "",
sewardjb51f0f42005-07-18 11:38:02 +00004353//zz frD_addr, frB_addr);
4354//zz DIP(" => not implemented\n");
4355//zz // CAB: Can we use one of the 128 bit SIMD Iop_Recip32F ops?
4356//zz return False;
sewardje14bb9f2005-07-22 09:39:02 +00004357
4358 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
4359 if (frB_addr != 0) {
4360 vex_printf("dis_fp_arith(PPC32)(instr,fmuls)\n");
4361 return False;
4362 }
cerion76de5cf2005-11-18 18:25:12 +00004363 DIP("fmuls%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004364 frD_addr, frA_addr, frC_addr);
4365 assign( frD, roundToSgl( binop(Iop_MulF64, mkexpr(frA), mkexpr(frC)) ));
4366 break;
4367
4368 default:
4369 vex_printf("dis_fp_arith(PPC32)(3B: opc2)\n");
4370 return False;
4371 }
4372 break;
cerion094d1392005-06-20 13:45:57 +00004373
cerion3d870a32005-03-18 12:23:33 +00004374 case 0x3F:
4375 switch (opc2) {
sewardje14bb9f2005-07-22 09:39:02 +00004376 case 0x12: // fdiv (Floating Divide (Double-Precision), PPC32 p406)
4377 if (frC_addr != 0) {
4378 vex_printf("dis_fp_arith(PPC32)(instr,fdiv)\n");
4379 return False;
4380 }
cerion76de5cf2005-11-18 18:25:12 +00004381 DIP("fdiv%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004382 frD_addr, frA_addr, frB_addr);
4383 assign( frD, binop( Iop_DivF64, mkexpr(frA), mkexpr(frB) ) );
4384 break;
4385
4386 case 0x14: // fsub (Floating Subtract (Double-Precision), PPC32 p429)
4387 if (frC_addr != 0) {
4388 vex_printf("dis_fp_arith(PPC32)(instr,fsub)\n");
4389 return False;
4390 }
cerion76de5cf2005-11-18 18:25:12 +00004391 DIP("fsub%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004392 frD_addr, frA_addr, frB_addr);
4393 assign( frD, binop( Iop_SubF64, mkexpr(frA), mkexpr(frB) ) );
4394 break;
cerion3d870a32005-03-18 12:23:33 +00004395
4396 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
4397 if (frC_addr != 0) {
cerion094d1392005-06-20 13:45:57 +00004398 vex_printf("dis_fp_arith(PPC32)(instr,fadd)\n");
cerion3d870a32005-03-18 12:23:33 +00004399 return False;
4400 }
cerion76de5cf2005-11-18 18:25:12 +00004401 DIP("fadd%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
cerion3d870a32005-03-18 12:23:33 +00004402 frD_addr, frA_addr, frB_addr);
cerion094d1392005-06-20 13:45:57 +00004403 assign( frD, binop( Iop_AddF64, mkexpr(frA), mkexpr(frB) ) );
4404 break;
cerion3d870a32005-03-18 12:23:33 +00004405
sewardjb51f0f42005-07-18 11:38:02 +00004406//zz case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
4407//zz if (frA_addr != 0 || frC_addr != 0) {
4408//zz vex_printf("dis_fp_arith(PPC32)(instr,fsqrt)\n");
4409//zz return False;
4410//zz }
cerion76de5cf2005-11-18 18:25:12 +00004411//zz DIP("fsqrt%s fr%d,fr%d\n", flag_rC ? "." : "",
sewardjb51f0f42005-07-18 11:38:02 +00004412//zz frD_addr, frB_addr);
4413//zz assign( frD, unop( Iop_SqrtF64, mkexpr(frB) ) );
4414//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00004415
4416 case 0x17: { // fsel (Floating Select, PPC32 p426)
4417 IRTemp cc = newTemp(Ity_I32);
4418 IRTemp cc_b0 = newTemp(Ity_I32);
4419
cerion76de5cf2005-11-18 18:25:12 +00004420 DIP("fsel%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004421 frD_addr, frA_addr, frC_addr, frB_addr);
4422
4423 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
4424 // => GT|EQ == (cc & 0x1 == 0)
4425 assign( cc, binop(Iop_CmpF64, mkexpr(frA), IRExpr_Const(IRConst_F64(0))) );
4426 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
4427
4428 // frD = (frA >= 0.0) ? frC : frB
4429 // = (cc_b0 == 0) ? frC : frB
4430 assign( frD,
4431 IRExpr_Mux0X(
4432 unop(Iop_1Uto8,
4433 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
4434 mkexpr(frB),
4435 mkexpr(frC) ));
4436 break;
4437 }
4438
4439 case 0x19: // fmul (Floating Multiply (Double Precision), PPC32 p413)
4440 if (frB_addr != 0) {
4441 vex_printf("dis_fp_arith(PPC32)(instr,fmul)\n");
4442 return False;
4443 }
cerion76de5cf2005-11-18 18:25:12 +00004444 DIP("fmul%s fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004445 frD_addr, frA_addr, frC_addr);
4446 assign( frD, binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ) );
4447 break;
4448
sewardjb51f0f42005-07-18 11:38:02 +00004449//zz case 0x1A: // frsqrte (Floating Reciprocal SqRt Estimate, PPC32 p424)
4450//zz if (frA_addr != 0 || frC_addr != 0) {
4451//zz vex_printf("dis_fp_arith(PPC32)(instr,frsqrte)\n");
4452//zz return False;
4453//zz }
cerion76de5cf2005-11-18 18:25:12 +00004454//zz DIP("frsqrte%s fr%d,fr%d\n", flag_rC ? "." : "",
sewardjb51f0f42005-07-18 11:38:02 +00004455//zz frD_addr, frB_addr);
4456//zz DIP(" => not implemented\n");
4457//zz // CAB: Iop_SqrtF64, then one of the 128 bit SIMD Iop_Recip32F ops?
4458//zz return False;
cerion3d870a32005-03-18 12:23:33 +00004459
4460 default:
4461 vex_printf("dis_fp_arith(PPC32)(3F: opc2)\n");
4462 return False;
4463 }
cerion094d1392005-06-20 13:45:57 +00004464 break;
4465
cerion3d870a32005-03-18 12:23:33 +00004466 default:
4467 vex_printf("dis_fp_arith(PPC32)(opc1)\n");
4468 return False;
4469 }
cerion094d1392005-06-20 13:45:57 +00004470
4471 putFReg( frD_addr, mkexpr(frD) );
cerion3d870a32005-03-18 12:23:33 +00004472 return True;
4473}
4474
4475
4476
sewardje14bb9f2005-07-22 09:39:02 +00004477/*
4478 Floating Point Mult-Add Instructions
4479*/
4480static Bool dis_fp_multadd ( UInt theInstr )
4481{
4482 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00004483 UChar opc1 = ifieldOPC(theInstr);
4484 UChar frD_addr = ifieldRegDS(theInstr);
4485 UChar frA_addr = ifieldRegA(theInstr);
4486 UChar frB_addr = ifieldRegB(theInstr);
4487 UChar frC_addr = ifieldRegC(theInstr);
4488 UChar opc2 = ifieldOPClo5(theInstr);
4489 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00004490
4491 IRTemp frD = newTemp(Ity_F64);
4492 IRTemp frA = newTemp(Ity_F64);
4493 IRTemp frB = newTemp(Ity_F64);
4494 IRTemp frC = newTemp(Ity_F64);
4495
4496 assign( frA, getFReg(frA_addr));
4497 assign( frB, getFReg(frB_addr));
4498 assign( frC, getFReg(frC_addr));
4499
4500 switch (opc1) {
4501 case 0x3B:
4502 switch (opc2) {
4503 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion76de5cf2005-11-18 18:25:12 +00004504 DIP("fmsubs%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004505 frD_addr, frA_addr, frC_addr, frB_addr);
4506 assign( frD, roundToSgl(
4507 binop( Iop_SubF64,
4508 binop(Iop_MulF64, mkexpr(frA), mkexpr(frC)),
4509 mkexpr(frB)) ));
4510 break;
4511
4512 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion76de5cf2005-11-18 18:25:12 +00004513 DIP("fmadds%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004514 frD_addr, frA_addr, frC_addr, frB_addr);
4515 assign( frD, roundToSgl(
4516 binop( Iop_AddF64,
4517 binop(Iop_MulF64, mkexpr(frA), mkexpr(frC)),
4518 mkexpr(frB)) ));
4519 break;
4520
4521 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion76de5cf2005-11-18 18:25:12 +00004522 DIP("fnmsubs%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004523 frD_addr, frA_addr, frC_addr, frB_addr);
4524 assign( frD, roundToSgl(
4525 unop(Iop_NegF64,
4526 binop(Iop_SubF64,
4527 binop(Iop_MulF64, mkexpr(frA), mkexpr(frC)),
4528 mkexpr(frB))) ));
4529 break;
4530
4531 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion76de5cf2005-11-18 18:25:12 +00004532 DIP("fnmadds%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004533 frD_addr, frA_addr, frC_addr, frB_addr);
4534 assign( frD, roundToSgl(
4535 unop(Iop_NegF64,
4536 binop(Iop_AddF64,
4537 binop(Iop_MulF64, mkexpr(frA), mkexpr(frC)),
4538 mkexpr(frB))) ));
4539 break;
4540
4541 default:
4542 vex_printf("dis_fp_multadd(PPC32)(3B: opc2)\n");
4543 return False;
4544 }
4545 break;
4546
4547 case 0x3F:
4548 switch (opc2) {
4549 case 0x1C: // fmsub (Float Mult-Subtr (Double Precision), PPC32 p411)
cerion76de5cf2005-11-18 18:25:12 +00004550 DIP("fmsub%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004551 frD_addr, frA_addr, frC_addr, frB_addr);
4552 assign( frD, binop( Iop_SubF64,
4553 binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ),
4554 mkexpr(frB) ));
4555 break;
4556
4557 case 0x1D: // fmadd (Float Mult-Add (Double Precision), PPC32 p408)
cerion76de5cf2005-11-18 18:25:12 +00004558 DIP("fmadd%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardje14bb9f2005-07-22 09:39:02 +00004559 frD_addr, frA_addr, frC_addr, frB_addr);
4560 assign( frD, binop( Iop_AddF64,
4561 binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ),
4562 mkexpr(frB) ));
4563 break;
4564
sewardj0e2cc672005-07-29 21:58:51 +00004565 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Double Precision), PPC32 p419)
cerion76de5cf2005-11-18 18:25:12 +00004566 DIP("fnmsub%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardj0e2cc672005-07-29 21:58:51 +00004567 frD_addr, frA_addr, frC_addr, frB_addr);
4568 assign( frD, unop( Iop_NegF64,
4569 binop( Iop_SubF64,
4570 binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ),
4571 mkexpr(frB) )));
4572 break;
4573
4574 case 0x1F: // fnmadd (Float Neg Mult-Add (Double Precision), PPC32 p417)
cerion76de5cf2005-11-18 18:25:12 +00004575 DIP("fnmadd%s fr%d,fr%d,fr%d,fr%d\n", flag_rC ? "." : "",
sewardj0e2cc672005-07-29 21:58:51 +00004576 frD_addr, frA_addr, frC_addr, frB_addr);
4577 assign( frD, unop( Iop_NegF64,
4578 binop( Iop_AddF64,
4579 binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ),
4580 mkexpr(frB) )));
4581 break;
sewardje14bb9f2005-07-22 09:39:02 +00004582
4583 default:
4584 vex_printf("dis_fp_multadd(PPC32)(3F: opc2)\n");
4585 return False;
4586 }
4587 break;
4588
4589 default:
4590 vex_printf("dis_fp_multadd(PPC32)(opc1)\n");
4591 return False;
4592 }
4593
4594 putFReg( frD_addr, mkexpr(frD) );
4595 return True;
4596}
4597
4598
4599
4600/*
4601 Floating Point Compare Instructions
4602*/
4603static Bool dis_fp_cmp ( UInt theInstr )
4604{
4605 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004606 UChar opc1 = ifieldOPC(theInstr);
4607 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
4608 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
4609 UChar frA_addr = ifieldRegA(theInstr);
4610 UChar frB_addr = ifieldRegB(theInstr);
4611 UInt opc2 = ifieldOPClo10(theInstr);
4612 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00004613
4614 IRTemp ccIR = newTemp(Ity_I32);
4615 IRTemp ccPPC32 = newTemp(Ity_I32);
4616
sewardje14bb9f2005-07-22 09:39:02 +00004617 IRTemp frA = newTemp(Ity_F64);
4618 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00004619
4620 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
4621 vex_printf("dis_fp_cmp(PPC32)(instr)\n");
4622 return False;
4623 }
4624
4625 assign( frA, getFReg(frA_addr));
4626 assign( frB, getFReg(frB_addr));
4627
4628 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
4629
4630 /* Map compare result from IR to PPC32 */
4631 /*
4632 FP cmp result | PPC | IR
4633 --------------------------
4634 UN | 0x1 | 0x45
4635 EQ | 0x2 | 0x40
4636 GT | 0x4 | 0x00
4637 LT | 0x8 | 0x01
4638 */
4639
4640 // ccPPC32 = Shl(1, (0x2 & ~(ccIR>>5)) || (0x1 & (XOR(ccIR, ccIR>>6))))
4641 assign(
4642 ccPPC32,
4643 binop(Iop_Shl32, mkU32(1),
4644 unop(Iop_32to8,
4645 binop(Iop_Or32,
4646 binop(Iop_And32, mkU32(2),
4647 unop(Iop_Not32,
4648 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5)))),
4649 binop(Iop_And32, mkU32(1),
4650 binop(Iop_Xor32, mkexpr(ccIR),
4651 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6)))))))
4652 );
4653
cerionedf7fc52005-11-18 20:57:41 +00004654 putSPR_field( PPC32_SPR_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00004655
cerionedf7fc52005-11-18 20:57:41 +00004656 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
4657 putSPR_field( PPC32_SPR_FPSCR, mkexpr(ccPPC32), 4 );
4658 */
sewardje14bb9f2005-07-22 09:39:02 +00004659
cerionedf7fc52005-11-18 20:57:41 +00004660 /* Note: Differences between fcmpu and fcmpo are only in exception
4661 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00004662 switch (opc2) {
4663 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
4664 DIP("fcmpu crf%d,fr%d,fr%d\n", crfD, frA_addr, frB_addr);
4665 break;
sewardje14bb9f2005-07-22 09:39:02 +00004666 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
4667 DIP("fcmpo crf%d,fr%d,fr%d\n", crfD, frA_addr, frB_addr);
4668 break;
4669 default:
4670 vex_printf("dis_fp_cmp(PPC32)(opc2)\n");
4671 return False;
4672 }
4673 return True;
4674}
4675
4676
4677
4678/*
4679 Floating Point Rounding/Conversion Instructions
4680*/
4681static Bool dis_fp_round ( UInt theInstr )
4682{
4683 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004684 UChar opc1 = ifieldOPC(theInstr);
4685 UChar frD_addr = ifieldRegDS(theInstr);
4686 UChar b16to20 = ifieldRegA(theInstr);
4687 UChar frB_addr = ifieldRegB(theInstr);
4688 UInt opc2 = ifieldOPClo10(theInstr);
4689 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00004690
4691 IRTemp frD = newTemp(Ity_F64);
4692 IRTemp frB = newTemp(Ity_F64);
4693 IRTemp r_tmp = newTemp(Ity_I32);
4694
4695 if (opc1 != 0x3F || b16to20 != 0) {
4696 vex_printf("dis_fp_round(PPC32)(instr)\n");
4697 return False;
4698 }
4699
4700 assign( frB, getFReg(frB_addr));
4701
sewardje14bb9f2005-07-22 09:39:02 +00004702 switch (opc2) {
sewardje14bb9f2005-07-22 09:39:02 +00004703 case 0x00C: // frsp (Floating Round to Single, PPC32 p423)
cerion76de5cf2005-11-18 18:25:12 +00004704 DIP("frsp%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004705 assign( frD, roundToSgl( mkexpr(frB) ));
4706 break;
4707
sewardj5f63c0c2005-09-09 10:36:55 +00004708 case 0x00E: // fctiw (Floating Conv to Int, PPC32 p404)
cerion76de5cf2005-11-18 18:25:12 +00004709 DIP("fctiw%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardj5f63c0c2005-09-09 10:36:55 +00004710 assign( r_tmp, binop(Iop_F64toI32, get_roundingmode(), mkexpr(frB)) );
4711 assign( frD, unop( Iop_ReinterpI64asF64,
4712 unop( Iop_32Uto64, mkexpr(r_tmp))));
4713 break;
sewardje14bb9f2005-07-22 09:39:02 +00004714
4715 case 0x00F: // fctiwz (Floating Conv to Int, Round to Zero, PPC32 p405)
cerion76de5cf2005-11-18 18:25:12 +00004716 DIP("fctiwz%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004717 assign( r_tmp, binop(Iop_F64toI32, mkU32(0x3), mkexpr(frB)) );
4718 assign( frD, unop( Iop_ReinterpI64asF64,
4719 unop( Iop_32Uto64, mkexpr(r_tmp))));
4720 break;
4721
4722 default:
4723 vex_printf("dis_fp_round(PPC32)(opc2)\n");
4724 return False;
4725 }
4726
4727 putFReg( frD_addr, mkexpr(frD) );
4728 return True;
4729}
4730
4731
4732
4733/*
4734 Floating Point Move Instructions
4735*/
4736static Bool dis_fp_move ( UInt theInstr )
4737{
4738 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004739 UChar opc1 = ifieldOPC(theInstr);
4740 UChar frD_addr = ifieldRegDS(theInstr);
4741 UChar b16to20 = ifieldRegA(theInstr);
4742 UChar frB_addr = ifieldRegB(theInstr);
4743 UInt opc2 = ifieldOPClo10(theInstr);
4744 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00004745
4746 IRTemp frD = newTemp(Ity_F64);
4747 IRTemp frB = newTemp(Ity_F64);
4748
4749 if (opc1 != 0x3F || b16to20 != 0) {
4750 vex_printf("dis_fp_move(PPC32)(instr)\n");
4751 return False;
4752 }
4753
4754 assign( frB, getFReg(frB_addr));
4755
sewardje14bb9f2005-07-22 09:39:02 +00004756 switch (opc2) {
sewardje14bb9f2005-07-22 09:39:02 +00004757 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion76de5cf2005-11-18 18:25:12 +00004758 DIP("fneg%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004759 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
4760 break;
4761
4762 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion76de5cf2005-11-18 18:25:12 +00004763 DIP("fmr%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004764 assign( frD, mkexpr(frB) );
4765 break;
4766
sewardj0e2cc672005-07-29 21:58:51 +00004767 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion76de5cf2005-11-18 18:25:12 +00004768 DIP("fnabs%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardj0e2cc672005-07-29 21:58:51 +00004769 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
4770 break;
sewardje14bb9f2005-07-22 09:39:02 +00004771
4772 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion76de5cf2005-11-18 18:25:12 +00004773 DIP("fabs%s fr%d,fr%d\n", flag_rC ? "." : "", frD_addr, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004774 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
4775 break;
4776
4777 default:
4778 vex_printf("dis_fp_move(PPC32)(opc2)\n");
4779 return False;
4780 }
4781
4782 putFReg( frD_addr, mkexpr(frD) );
4783 return True;
4784}
4785
4786
4787
4788/*
4789 Floating Point Status/Control Register Instructions
4790*/
4791static Bool dis_fp_scr ( UInt theInstr )
4792{
cerion76de5cf2005-11-18 18:25:12 +00004793 /* Many forms - see each switch case */
4794 UChar opc1 = ifieldOPC(theInstr);
4795 UInt opc2 = ifieldOPClo10(theInstr);
4796 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00004797
4798 if (opc1 != 0x3F) {
4799 vex_printf("dis_fp_scr(PPC32)(instr)\n");
4800 return False;
4801 }
4802
4803 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00004804//zz case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
4805//zz // Bit crbD of the FPSCR is set.
cerion76de5cf2005-11-18 18:25:12 +00004806//zz UChar crbD = ifieldRegDS(theInstr);
4807//zz UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardjb51f0f42005-07-18 11:38:02 +00004808//zz
4809//zz if (b11to20 != 0) {
4810//zz vex_printf("dis_fp_scr(PPC32)(instr,mtfsb1)\n");
4811//zz return False;
4812//zz }
cerion76de5cf2005-11-18 18:25:12 +00004813//zz DIP("mtfsb1%s crb%d \n", flag_rC ? "." : "", crbD);
cerionedf7fc52005-11-18 20:57:41 +00004814//zz putSPR_masked( PPC32_SPR_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
sewardjb51f0f42005-07-18 11:38:02 +00004815//zz break;
4816//zz }
4817//zz
4818//zz case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
cerion76de5cf2005-11-18 18:25:12 +00004819//zz UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
4820//zz UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
4821//zz UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
4822//zz UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
sewardjb51f0f42005-07-18 11:38:02 +00004823//zz
4824//zz IRTemp tmp = newTemp(Ity_I32);
4825//zz
cerion76de5cf2005-11-18 18:25:12 +00004826//zz if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
sewardjb51f0f42005-07-18 11:38:02 +00004827//zz vex_printf("dis_fp_scr(PPC32)(instr,mcrfs)\n");
4828//zz return False;
4829//zz }
4830//zz DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
cerionedf7fc52005-11-18 20:57:41 +00004831//zz assign( tmp, getSPR_field( PPC32_SPR_FPSCR, crfS ) );
4832//zz putSPR_field( PPC32_SPR_CR, mkexpr(tmp), crfD );
sewardjb51f0f42005-07-18 11:38:02 +00004833//zz break;
4834//zz }
sewardj0e2cc672005-07-29 21:58:51 +00004835
4836 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
4837 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00004838 UChar crbD = ifieldRegDS(theInstr);
4839 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00004840
4841 if (b11to20 != 0) {
4842 vex_printf("dis_fp_scr(PPC32)(instr,mtfsb0)\n");
4843 return False;
4844 }
cerion76de5cf2005-11-18 18:25:12 +00004845 DIP("mtfsb0%s crb%d\n", flag_rC ? "." : "", crbD);
cerionedf7fc52005-11-18 20:57:41 +00004846 putSPR_masked( PPC32_SPR_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00004847 break;
4848 }
4849
4850 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00004851 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
4852 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
4853 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
4854 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00004855
4856 if (b16to22 != 0 || b11 != 0) {
4857 vex_printf("dis_fp_scr(PPC32)(instr,mtfsfi)\n");
4858 return False;
4859 }
cerion76de5cf2005-11-18 18:25:12 +00004860 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? "." : "", crfD, IMM);
cerionedf7fc52005-11-18 20:57:41 +00004861 putSPR_field( PPC32_SPR_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00004862 break;
4863 }
sewardje14bb9f2005-07-22 09:39:02 +00004864
4865 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
cerion76de5cf2005-11-18 18:25:12 +00004866 UChar frD_addr = ifieldRegDS(theInstr);
4867 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardje14bb9f2005-07-22 09:39:02 +00004868
4869 if (b11to20 != 0) {
4870 vex_printf("dis_fp_scr(PPC32)(instr,mffs)\n");
4871 return False;
4872 }
cerion76de5cf2005-11-18 18:25:12 +00004873 DIP("mffs%s fr%d\n", flag_rC ? "." : "", frD_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004874 putFReg( frD_addr, unop( Iop_ReinterpI64asF64,
4875 unop( Iop_32Uto64,
cerionedf7fc52005-11-18 20:57:41 +00004876 getSPR_masked( PPC32_SPR_FPSCR, 0x3 ) )));
sewardje14bb9f2005-07-22 09:39:02 +00004877 break;
4878 }
4879
4880 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00004881 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
4882 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
4883 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
4884 UChar frB_addr = ifieldRegB(theInstr);
4885 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00004886 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004887 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00004888
4889 if (b25 != 0 || b16 != 0) {
4890 vex_printf("dis_fp_scr(PPC32)(instr,mtfsf)\n");
4891 return False;
4892 }
cerion76de5cf2005-11-18 18:25:12 +00004893 DIP("mtfsf%s %d,fr%d\n", flag_rC ? "." : "", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00004894 assign( frB, getFReg(frB_addr));
4895 assign( rB_32, unop( Iop_64to32,
4896 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
4897 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00004898 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00004899 for (i=0; i<8; i++) {
4900 if ((FM & (1<<(7-i))) == 1) {
4901 mask |= 0xF << (7-i);
4902 }
4903 }
cerionedf7fc52005-11-18 20:57:41 +00004904 putSPR_masked( PPC32_SPR_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00004905 break;
4906 }
4907
4908 default:
4909 vex_printf("dis_fp_scr(PPC32)(opc2)\n");
4910 return False;
4911 }
4912 return True;
4913}
4914
4915
4916
cerion32aad402005-09-10 12:02:24 +00004917/*------------------------------------------------------------*/
4918/*--- AltiVec Instruction Translation ---*/
4919/*------------------------------------------------------------*/
4920
4921/*
4922 Altivec Cache Control Instructions (Data Streams)
4923*/
4924static Bool dis_av_datastream ( UInt theInstr )
4925{
cerion76de5cf2005-11-18 18:25:12 +00004926 /* X-Form */
4927 UChar opc1 = ifieldOPC(theInstr);
4928 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
4929 UChar flag_A = flag_T;
4930 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
4931 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
4932 UChar rA_addr = ifieldRegA(theInstr);
4933 UChar rB_addr = ifieldRegB(theInstr);
4934 UInt opc2 = ifieldOPClo10(theInstr);
4935 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00004936
4937 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
4938 vex_printf("dis_av_datastream(PPC32)(instr)\n");
4939 return False;
4940 }
4941
4942 switch (opc2) {
4943 case 0x156: // dst (Data Stream Touch, AV p115)
4944 DIP("dst%s r%d,r%d,%d\n", flag_T ? "t" : "", rA_addr, rB_addr, STRM);
4945 DIP(" => not implemented\n");
4946 return False;
4947
4948 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
4949 DIP("dstst%s r%d,r%d,%d\n", flag_T ? "t" : "", rA_addr, rB_addr, STRM);
4950 DIP(" => not implemented\n");
4951 return False;
4952
4953 case 0x336: // dss (Data Stream Stop, AV p114)
4954 if (rA_addr != 0 || rB_addr != 0) {
4955 vex_printf("dis_av_datastream(PPC32)(opc2,dst)\n");
4956 return False;
4957 }
4958 if (flag_A == 0) {
4959 DIP("dss %d\n", STRM);
4960 DIP(" => not implemented\n");
4961 } else {
4962 DIP("dssall\n");
4963 DIP(" => not implemented\n");
4964 }
4965 return False;
4966
4967 default:
4968 vex_printf("dis_av_datastream(PPC32)(opc2)\n");
4969 return False;
4970 }
4971 return True;
4972}
4973
4974/*
4975 AltiVec Processor Control Instructions
4976*/
4977static Bool dis_av_procctl ( UInt theInstr )
4978{
cerion76de5cf2005-11-18 18:25:12 +00004979 /* VX-Form */
4980 UChar opc1 = ifieldOPC(theInstr);
4981 UChar vD_addr = ifieldRegDS(theInstr);
4982 UChar vA_addr = ifieldRegA(theInstr);
4983 UChar vB_addr = ifieldRegB(theInstr);
4984 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00004985
4986 if (opc1 != 0x4) {
4987 vex_printf("dis_av_procctl(PPC32)(instr)\n");
4988 return False;
4989 }
4990
4991 switch (opc2) {
4992 case 0x604: // mfvscr (Move from VSCR, AV p129)
4993 if (vA_addr != 0 || vB_addr != 0) {
4994 vex_printf("dis_av_procctl(PPC32)(opc2,dst)\n");
4995 return False;
4996 }
4997 DIP("mfvscr v%d\n", vD_addr);
cerion225a0342005-09-12 20:49:09 +00004998 putVReg( vD_addr, unop(Iop_32UtoV128, getSPR( PPC32_SPR_VSCR )) );
4999 break;
cerion32aad402005-09-10 12:02:24 +00005000
cerion225a0342005-09-12 20:49:09 +00005001 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00005002 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00005003 if (vD_addr != 0 || vA_addr != 0) {
5004 vex_printf("dis_av_procctl(PPC32)(opc2,dst)\n");
5005 return False;
5006 }
5007 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00005008 assign( vB, getVReg(vB_addr));
5009 putSPR( PPC32_SPR_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
5010 break;
5011 }
cerion32aad402005-09-10 12:02:24 +00005012 default:
5013 vex_printf("dis_av_procctl(PPC32)(opc2)\n");
5014 return False;
5015 }
5016 return True;
5017}
ceriona982c052005-06-28 17:23:09 +00005018
5019/*
5020 AltiVec Load Instructions
5021*/
5022static Bool dis_av_load ( UInt theInstr )
5023{
cerion76de5cf2005-11-18 18:25:12 +00005024 /* X-Form */
5025 UChar opc1 = ifieldOPC(theInstr);
5026 UChar vD_addr = ifieldRegDS(theInstr);
5027 UChar rA_addr = ifieldRegA(theInstr);
5028 UChar rB_addr = ifieldRegB(theInstr);
5029 UInt opc2 = ifieldOPClo10(theInstr);
5030 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00005031
ceriona50fde52005-07-01 21:16:10 +00005032 IRTemp EA = newTemp(Ity_I32);
5033 IRTemp EA_aligned = newTemp(Ity_I32);
5034
ceriona982c052005-06-28 17:23:09 +00005035 if (opc1 != 0x1F || b0 != 0) {
5036 vex_printf("dis_av_load(PPC32)(instr)\n");
5037 return False;
5038 }
5039
sewardjafe85832005-09-09 10:25:39 +00005040 assign( EA, ea_standard(rA_addr, rB_addr) );
ceriona50fde52005-07-01 21:16:10 +00005041
ceriona982c052005-06-28 17:23:09 +00005042 switch (opc2) {
5043
cerion6f6c6a02005-09-13 18:41:09 +00005044 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
sewardjd1470942005-10-22 02:01:16 +00005045 UInt vD_off = vectorGuestRegOffset(vD_addr);
5046 IRExpr** args = mkIRExprVec_3(
5047 mkU32(vD_off),
5048 binop(Iop_And32, mkexpr(EA), mkU32(0xF)),
5049 mkU32(0)/*left*/ );
cerion6f6c6a02005-09-13 18:41:09 +00005050 IRDirty* d = unsafeIRDirty_0_N (
5051 0/*regparms*/,
5052 "ppc32g_dirtyhelper_LVS",
5053 &ppc32g_dirtyhelper_LVS,
5054 args );
sewardj197bd172005-10-12 11:34:33 +00005055 DIP("lvsl v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00005056 /* declare guest state effects */
5057 d->needsBBP = True;
5058 d->nFxState = 1;
5059 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00005060 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00005061 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00005062
cerion6f6c6a02005-09-13 18:41:09 +00005063 /* execute the dirty call, side-effecting guest state */
5064 stmt( IRStmt_Dirty(d) );
5065 break;
5066 }
5067 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
sewardjd1470942005-10-22 02:01:16 +00005068 UInt vD_off = vectorGuestRegOffset(vD_addr);
5069 IRExpr** args = mkIRExprVec_3(
5070 mkU32(vD_off),
5071 binop(Iop_And32, mkexpr(EA), mkU32(0xF)),
5072 mkU32(1)/*right*/ );
cerion6f6c6a02005-09-13 18:41:09 +00005073 IRDirty* d = unsafeIRDirty_0_N (
5074 0/*regparms*/,
5075 "ppc32g_dirtyhelper_LVS",
5076 &ppc32g_dirtyhelper_LVS,
5077 args );
sewardj197bd172005-10-12 11:34:33 +00005078 DIP("lvsr v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00005079 /* declare guest state effects */
5080 d->needsBBP = True;
5081 d->nFxState = 1;
5082 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00005083 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00005084 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00005085
cerion6f6c6a02005-09-13 18:41:09 +00005086 /* execute the dirty call, side-effecting guest state */
5087 stmt( IRStmt_Dirty(d) );
5088 break;
5089 }
cerion32aad402005-09-10 12:02:24 +00005090 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
5091 DIP("lvebx v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005092 /* loads addressed byte into vector[EA[0:3]
5093 since all other destination bytes are undefined,
5094 can simply load entire vector from 16-aligned EA */
5095 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFF0) ));
5096 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_aligned)) );
5097 break;
cerion32aad402005-09-10 12:02:24 +00005098
5099 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
5100 DIP("lvehx v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005101 /* see note for lvebx */
5102 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFF0) ));
5103 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_aligned)) );
5104 break;
cerion32aad402005-09-10 12:02:24 +00005105
5106 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
5107 DIP("lvewx v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005108 /* see note for lvebx */
5109 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFF0) ));
5110 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_aligned)) );
5111 break;
ceriona982c052005-06-28 17:23:09 +00005112
5113 case 0x067: // lvx (Load Vector Indexed, AV p127)
5114 DIP("lvx v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
ceriona50fde52005-07-01 21:16:10 +00005115 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFF0) ));
5116 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_aligned)) );
5117 break;
ceriona982c052005-06-28 17:23:09 +00005118
cerion32aad402005-09-10 12:02:24 +00005119 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
5120 // XXX: lvxl gives explicit control over cache block replacement
5121 DIP("lvxl v%d,r%d,r%d\n", vD_addr, rA_addr, rB_addr);
5122 DIP(" => not implemented\n");
5123 return False;
ceriona982c052005-06-28 17:23:09 +00005124
5125 default:
5126 vex_printf("dis_av_load(PPC32)(opc2)\n");
5127 return False;
5128 }
5129 return True;
5130}
5131
5132/*
5133 AltiVec Store Instructions
5134*/
5135static Bool dis_av_store ( UInt theInstr )
5136{
cerion76de5cf2005-11-18 18:25:12 +00005137 /* X-Form */
5138 UChar opc1 = ifieldOPC(theInstr);
5139 UChar vS_addr = ifieldRegDS(theInstr);
5140 UChar rA_addr = ifieldRegA(theInstr);
5141 UChar rB_addr = ifieldRegB(theInstr);
5142 UInt opc2 = ifieldOPClo10(theInstr);
5143 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00005144
cerionedf7fc52005-11-18 20:57:41 +00005145 IRTemp vS = newTemp(Ity_V128);
5146 IRTemp EA = newTemp(Ity_I32);
ceriona982c052005-06-28 17:23:09 +00005147 IRTemp EA_aligned = newTemp(Ity_I32);
5148
ceriona982c052005-06-28 17:23:09 +00005149 assign( vS, getVReg(vS_addr));
sewardjafe85832005-09-09 10:25:39 +00005150 assign( EA, ea_standard(rA_addr, rB_addr) );
ceriona982c052005-06-28 17:23:09 +00005151
5152 if (opc1 != 0x1F || b0 != 0) {
5153 vex_printf("dis_av_store(PPC32)(instr)\n");
5154 return False;
5155 }
5156
5157 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00005158 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
cerion61c92742005-09-14 22:59:26 +00005159 IRTemp eb = newTemp(Ity_I8);
5160 IRTemp idx = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00005161 DIP("stvebx v%d,r%d,r%d\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005162 assign( eb, binop(Iop_And8, mkU8(0xF),
5163 unop(Iop_32to8, mkexpr(EA) )) );
5164 assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
5165 mkU8(3)) );
5166 storeBE( mkexpr(EA),
5167 unop(Iop_32to8, unop(Iop_V128to32,
5168 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
5169 break;
5170 }
5171 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
5172 IRTemp eb = newTemp(Ity_I8);
5173 IRTemp idx = newTemp(Ity_I8);
cerion32aad402005-09-10 12:02:24 +00005174 DIP("stvehx v%d,r%d,r%d\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005175 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFFE) ));
5176 assign( eb, binop(Iop_And8, mkU8(0xF),
5177 unop(Iop_32to8, mkexpr(EA_aligned) )) );
5178 assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
5179 mkU8(3)) );
5180 storeBE( mkexpr(EA_aligned),
5181 unop(Iop_32to16, unop(Iop_V128to32,
5182 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
5183 break;
5184 }
5185 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
cerion61c92742005-09-14 22:59:26 +00005186 IRTemp eb = newTemp(Ity_I8);
5187 IRTemp idx = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00005188 DIP("stvewx v%d,r%d,r%d\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00005189 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFFC) ));
5190 assign( eb, binop(Iop_And8, mkU8(0xF),
5191 unop(Iop_32to8, mkexpr(EA_aligned) )) );
5192 assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
5193 mkU8(3)) );
5194 storeBE( mkexpr(EA_aligned),
5195 unop(Iop_V128to32,
5196 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
5197 break;
5198 }
cerion32aad402005-09-10 12:02:24 +00005199
ceriona982c052005-06-28 17:23:09 +00005200 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
5201 DIP("stvx v%d,r%d,r%d\n", vS_addr, rA_addr, rB_addr);
5202 assign( EA_aligned, binop( Iop_And32, mkexpr(EA), mkU32(0xFFFFFFF0) ));
5203 storeBE( mkexpr(EA_aligned), mkexpr(vS) );
5204 break;
5205
cerion32aad402005-09-10 12:02:24 +00005206 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
5207 // XXX: stvxl can give explicit control over cache block replacement
5208 DIP("stvxl v%d,r%d,r%d\n", vS_addr, rA_addr, rB_addr);
5209 DIP(" => not implemented\n");
5210 return False;
5211
5212// EA_aligned = EA & 0xFFFF_FFF0;
5213// STORE(vS, 16, EA);
ceriona982c052005-06-28 17:23:09 +00005214
5215 default:
5216 vex_printf("dis_av_store(PPC32)(opc2)\n");
5217 return False;
5218 }
5219 return True;
5220}
5221
cerion32aad402005-09-10 12:02:24 +00005222/*
5223 AltiVec Arithmetic Instructions
5224*/
5225static Bool dis_av_arith ( UInt theInstr )
5226{
cerion76de5cf2005-11-18 18:25:12 +00005227 /* VX-Form */
5228 UChar opc1 = ifieldOPC(theInstr);
5229 UChar vD_addr = ifieldRegDS(theInstr);
5230 UChar vA_addr = ifieldRegA(theInstr);
5231 UChar vB_addr = ifieldRegB(theInstr);
5232 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00005233
ceriond3e52412005-09-14 21:15:40 +00005234 IRTemp vA = newTemp(Ity_V128);
5235 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00005236 IRTemp z3 = newTemp(Ity_I64);
5237 IRTemp z2 = newTemp(Ity_I64);
5238 IRTemp z1 = newTemp(Ity_I64);
5239 IRTemp z0 = newTemp(Ity_I64);
5240 IRTemp aEvn, aOdd;
5241 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
5242 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
5243 IRTemp b3, b2, b1, b0;
5244
5245 aEvn = aOdd = IRTemp_INVALID;
5246 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
5247 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
5248 b3 = b2 = b1 = b0 = IRTemp_INVALID;
5249
ceriond3e52412005-09-14 21:15:40 +00005250 assign( vA, getVReg(vA_addr));
5251 assign( vB, getVReg(vB_addr));
5252
cerion32aad402005-09-10 12:02:24 +00005253 if (opc1 != 0x4) {
5254 vex_printf("dis_av_arith(PPC32)(opc1 != 0x4)\n");
5255 return False;
5256 }
5257
5258 switch (opc2) {
5259 /* Add */
ceriond3e52412005-09-14 21:15:40 +00005260 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00005261 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005262 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00005263 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00005264 binop(Iop_CmpGT32Ux4, mkexpr(vB),
5265 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00005266 mkU8(31)) );
5267 break;
5268 }
cerion32aad402005-09-10 12:02:24 +00005269 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
5270 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005271 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
5272 break;
5273
cerion32aad402005-09-10 12:02:24 +00005274 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
5275 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005276 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
5277 break;
5278
cerion32aad402005-09-10 12:02:24 +00005279 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
5280 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005281 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
5282 break;
5283
cerion32aad402005-09-10 12:02:24 +00005284 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
5285 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005286 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
5287 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
5288 break;
5289
cerion32aad402005-09-10 12:02:24 +00005290 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
5291 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005292 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
5293 // TODO: set VSCR[SAT]
5294 break;
5295
cerion32aad402005-09-10 12:02:24 +00005296 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
5297 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005298 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
5299 // TODO: set VSCR[SAT]
5300 break;
5301
cerion32aad402005-09-10 12:02:24 +00005302 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
5303 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005304 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
5305 // TODO: set VSCR[SAT]
5306 break;
5307
cerion32aad402005-09-10 12:02:24 +00005308 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
5309 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005310 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
5311 // TODO: set VSCR[SAT]
5312 break;
5313
cerion32aad402005-09-10 12:02:24 +00005314 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
5315 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005316 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
5317 // TODO: set VSCR[SAT]
5318 break;
5319
5320
cerion32aad402005-09-10 12:02:24 +00005321 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00005322 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00005323 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005324 /* unsigned_ov(x-y) = (y >u x) */
5325 putVReg( vD_addr, binop(Iop_ShrN32x4,
5326 unop(Iop_NotV128,
5327 binop(Iop_CmpGT32Ux4, mkexpr(vB),
5328 mkexpr(vA))),
5329 mkU8(31)) );
5330 break;
5331 }
cerion32aad402005-09-10 12:02:24 +00005332 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
5333 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005334 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
5335 break;
5336
cerion32aad402005-09-10 12:02:24 +00005337 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
5338 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005339 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
5340 break;
5341
cerion32aad402005-09-10 12:02:24 +00005342 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
5343 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005344 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
5345 break;
5346
cerion32aad402005-09-10 12:02:24 +00005347 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
5348 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005349 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
5350 // TODO: set VSCR[SAT]
5351 break;
5352
cerion32aad402005-09-10 12:02:24 +00005353 case 0x640: // vsubuhs (Subtract Unsigned Half Word Saturate, AV p268)
5354 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005355 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
5356 // TODO: set VSCR[SAT]
5357 break;
5358
cerion32aad402005-09-10 12:02:24 +00005359 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
5360 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005361 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
5362 // TODO: set VSCR[SAT]
5363 break;
5364
cerion32aad402005-09-10 12:02:24 +00005365 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
5366 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005367 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
5368 // TODO: set VSCR[SAT]
5369 break;
5370
cerion32aad402005-09-10 12:02:24 +00005371 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
5372 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005373 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
5374 // TODO: set VSCR[SAT]
5375 break;
5376
cerion32aad402005-09-10 12:02:24 +00005377 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
5378 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005379 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
5380 // TODO: set VSCR[SAT]
5381 break;
cerion32aad402005-09-10 12:02:24 +00005382
5383
5384 /* Maximum */
5385 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
5386 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005387 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
5388 break;
cerion32aad402005-09-10 12:02:24 +00005389
5390 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
5391 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005392 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
5393 break;
cerion32aad402005-09-10 12:02:24 +00005394
5395 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
5396 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005397 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
5398 break;
cerion32aad402005-09-10 12:02:24 +00005399
5400 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
5401 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005402 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
5403 break;
cerion32aad402005-09-10 12:02:24 +00005404
5405 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
5406 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005407 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
5408 break;
cerion32aad402005-09-10 12:02:24 +00005409
5410 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
5411 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005412 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
5413 break;
cerion32aad402005-09-10 12:02:24 +00005414
5415
5416 /* Minimum */
5417 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
5418 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005419 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
5420 break;
cerion32aad402005-09-10 12:02:24 +00005421
5422 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
5423 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005424 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
5425 break;
cerion32aad402005-09-10 12:02:24 +00005426
5427 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
5428 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005429 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
5430 break;
cerion32aad402005-09-10 12:02:24 +00005431
5432 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
5433 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005434 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
5435 break;
cerion32aad402005-09-10 12:02:24 +00005436
5437 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
5438 DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005439 putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
5440 break;
cerion32aad402005-09-10 12:02:24 +00005441
5442 case 0x382: // vminsw (Minimum Signed Word, AV p190)
5443 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005444 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
5445 break;
5446
cerion32aad402005-09-10 12:02:24 +00005447
5448 /* Average */
5449 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
5450 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005451 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
5452 break;
cerion32aad402005-09-10 12:02:24 +00005453
5454 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
5455 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005456 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
5457 break;
cerion32aad402005-09-10 12:02:24 +00005458
5459 case 0x482: // vavguw (Average Unsigned Word, AV p154)
5460 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005461 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
5462 break;
cerion32aad402005-09-10 12:02:24 +00005463
5464 case 0x502: // vavgsb (Average Signed Byte, AV p149)
5465 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005466 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
5467 break;
cerion32aad402005-09-10 12:02:24 +00005468
5469 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
5470 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005471 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
5472 break;
cerion32aad402005-09-10 12:02:24 +00005473
5474 case 0x582: // vavgsw (Average Signed Word, AV p151)
5475 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00005476 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
5477 break;
cerion32aad402005-09-10 12:02:24 +00005478
5479
5480 /* Multiply */
5481 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
5482 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005483 putVReg( vD_addr, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00005484 break;
cerion32aad402005-09-10 12:02:24 +00005485
5486 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
5487 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005488 putVReg( vD_addr, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00005489 break;
cerion32aad402005-09-10 12:02:24 +00005490
5491 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
5492 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005493 putVReg( vD_addr, binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00005494 break;
cerion32aad402005-09-10 12:02:24 +00005495
5496 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
5497 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005498 putVReg( vD_addr, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00005499 break;
cerion32aad402005-09-10 12:02:24 +00005500
5501 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
5502 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005503 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00005504 break;
cerion32aad402005-09-10 12:02:24 +00005505
5506 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
5507 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005508 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00005509 break;
cerion32aad402005-09-10 12:02:24 +00005510
5511 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
5512 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005513 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00005514 break;
cerion32aad402005-09-10 12:02:24 +00005515
5516 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
5517 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00005518 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00005519 break;
cerion32aad402005-09-10 12:02:24 +00005520
5521
5522 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00005523 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
5524 IRTemp aEE, aEO, aOE, aOO;
5525 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00005526 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00005527
cerion4a49b032005-11-08 16:23:07 +00005528 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
5529 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
5530 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
5531 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
5532
5533 /* break V128 to 4xI32's, zero-extending to I64's */
5534 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
5535 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
5536 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
5537 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
5538 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
5539
5540 /* add lanes */
5541 assign( z3, binop(Iop_Add64, mkexpr(b3),
5542 binop(Iop_Add64,
5543 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
5544 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
5545 assign( z2, binop(Iop_Add64, mkexpr(b2),
5546 binop(Iop_Add64,
5547 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
5548 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
5549 assign( z1, binop(Iop_Add64, mkexpr(b1),
5550 binop(Iop_Add64,
5551 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
5552 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
5553 assign( z0, binop(Iop_Add64, mkexpr(b0),
5554 binop(Iop_Add64,
5555 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
5556 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
5557
5558 /* saturate-narrow to 32bit, and combine to V128 */
5559 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
5560 mkexpr(z1), mkexpr(z0)) );
5561 break;
5562 }
5563 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
5564 IRTemp aEE, aEO, aOE, aOO;
5565 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00005566 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00005567
cerion4a49b032005-11-08 16:23:07 +00005568 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
5569 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
5570 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
5571 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
5572
5573 /* break V128 to 4xI32's, sign-extending to I64's */
5574 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
5575 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
5576 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
5577 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
5578 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
5579
5580 /* add lanes */
5581 assign( z3, binop(Iop_Add64, mkexpr(b3),
5582 binop(Iop_Add64,
5583 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
5584 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
5585 assign( z2, binop(Iop_Add64, mkexpr(b2),
5586 binop(Iop_Add64,
5587 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
5588 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
5589 assign( z1, binop(Iop_Add64, mkexpr(b1),
5590 binop(Iop_Add64,
5591 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
5592 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
5593 assign( z0, binop(Iop_Add64, mkexpr(b0),
5594 binop(Iop_Add64,
5595 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
5596 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
5597
5598 /* saturate-narrow to 32bit, and combine to V128 */
5599 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
5600 mkexpr(z1), mkexpr(z0)) );
5601 break;
5602 }
5603 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00005604 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00005605
cerion4a49b032005-11-08 16:23:07 +00005606 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
5607 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
5608
5609 /* break V128 to 4xI32's, sign-extending to I64's */
5610 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
5611 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
5612 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
5613
5614 /* add lanes */
5615 assign( z3, binop(Iop_Add64, mkexpr(b3),
5616 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
5617 assign( z2, binop(Iop_Add64, mkexpr(b2),
5618 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
5619 assign( z1, binop(Iop_Add64, mkexpr(b1),
5620 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
5621 assign( z0, binop(Iop_Add64, mkexpr(b0),
5622 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
5623
5624 /* saturate-narrow to 32bit, and combine to V128 */
5625 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
5626 mkexpr(z1), mkexpr(z0)) );
5627 break;
5628 }
5629 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00005630 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00005631
cerion4a49b032005-11-08 16:23:07 +00005632 /* break V128 to 4xI32's, sign-extending to I64's */
5633 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
5634 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
5635
5636 /* add lanes */
5637 assign( z2, binop(Iop_Add64, mkexpr(b2),
5638 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
5639 assign( z0, binop(Iop_Add64, mkexpr(b0),
5640 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
5641
5642 /* saturate-narrow to 32bit, and combine to V128 */
5643 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
5644 mkU64(0), mkexpr(z0)) );
5645 break;
5646 }
5647 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00005648 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00005649
cerion4a49b032005-11-08 16:23:07 +00005650 /* break V128 to 4xI32's, sign-extending to I64's */
5651 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
5652 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
5653
5654 /* add lanes */
5655 assign( z0, binop(Iop_Add64, mkexpr(b0),
5656 binop(Iop_Add64,
5657 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
5658 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
5659
5660 /* saturate-narrow to 32bit, and combine to V128 */
5661 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
5662 mkU64(0), mkexpr(z0)) );
5663 break;
5664 }
cerion32aad402005-09-10 12:02:24 +00005665 default:
5666 vex_printf("dis_av_arith(PPC32)(opc2=0x%x)\n", opc2);
5667 return False;
5668 }
5669 return True;
5670}
5671
5672/*
5673 AltiVec Logic Instructions
5674*/
5675static Bool dis_av_logic ( UInt theInstr )
5676{
cerion76de5cf2005-11-18 18:25:12 +00005677 /* VX-Form */
5678 UChar opc1 = ifieldOPC(theInstr);
5679 UChar vD_addr = ifieldRegDS(theInstr);
5680 UChar vA_addr = ifieldRegA(theInstr);
5681 UChar vB_addr = ifieldRegB(theInstr);
5682 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00005683
cerion225a0342005-09-12 20:49:09 +00005684 IRTemp vA = newTemp(Ity_V128);
5685 IRTemp vB = newTemp(Ity_V128);
5686 assign( vA, getVReg(vA_addr));
5687 assign( vB, getVReg(vB_addr));
5688
cerion32aad402005-09-10 12:02:24 +00005689 if (opc1 != 0x4) {
5690 vex_printf("dis_av_logic(PPC32)(opc1 != 0x4)\n");
5691 return False;
5692 }
5693
5694 switch (opc2) {
5695 case 0x404: // vand (And, AV p147)
5696 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00005697 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
5698 break;
cerion32aad402005-09-10 12:02:24 +00005699
5700 case 0x444: // vandc (And, AV p148)
5701 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00005702 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00005703 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00005704 break;
cerion32aad402005-09-10 12:02:24 +00005705
5706 case 0x484: // vor (Or, AV p217)
5707 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00005708 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
5709 break;
cerion32aad402005-09-10 12:02:24 +00005710
5711 case 0x4C4: // vxor (Xor, AV p282)
5712 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00005713 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
5714 break;
cerion32aad402005-09-10 12:02:24 +00005715
5716 case 0x504: // vnor (Nor, AV p216)
5717 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00005718 putVReg( vD_addr,
5719 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
5720 break;
cerion32aad402005-09-10 12:02:24 +00005721
5722 default:
5723 vex_printf("dis_av_logic(PPC32)(opc2=0x%x)\n", opc2);
5724 return False;
5725 }
5726 return True;
5727}
5728
5729/*
5730 AltiVec Compare Instructions
5731*/
5732static Bool dis_av_cmp ( UInt theInstr )
5733{
cerion76de5cf2005-11-18 18:25:12 +00005734 /* VXR-Form */
5735 UChar opc1 = ifieldOPC(theInstr);
5736 UChar vD_addr = ifieldRegDS(theInstr);
5737 UChar vA_addr = ifieldRegA(theInstr);
5738 UChar vB_addr = ifieldRegB(theInstr);
5739 UChar flag_rC = ifieldBIT10(theInstr);
5740 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00005741
cerion0c439222005-09-15 14:22:58 +00005742 IRTemp vA = newTemp(Ity_V128);
5743 IRTemp vB = newTemp(Ity_V128);
5744 IRTemp vD = newTemp(Ity_V128);
5745 assign( vA, getVReg(vA_addr));
5746 assign( vB, getVReg(vB_addr));
5747
cerion32aad402005-09-10 12:02:24 +00005748 if (opc1 != 0x4) {
5749 vex_printf("dis_av_cmp(PPC32)(instr)\n");
5750 return False;
5751 }
5752
5753 switch (opc2) {
5754 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion76de5cf2005-11-18 18:25:12 +00005755 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005756 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
5757 break;
cerion32aad402005-09-10 12:02:24 +00005758
5759 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion76de5cf2005-11-18 18:25:12 +00005760 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005761 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
5762 break;
cerion32aad402005-09-10 12:02:24 +00005763
5764 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion76de5cf2005-11-18 18:25:12 +00005765 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005766 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
5767 break;
cerion32aad402005-09-10 12:02:24 +00005768
5769 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion76de5cf2005-11-18 18:25:12 +00005770 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005771 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
5772 break;
cerion32aad402005-09-10 12:02:24 +00005773
5774 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion76de5cf2005-11-18 18:25:12 +00005775 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005776 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
5777 break;
cerion32aad402005-09-10 12:02:24 +00005778
5779 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion76de5cf2005-11-18 18:25:12 +00005780 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005781 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
5782 break;
cerion32aad402005-09-10 12:02:24 +00005783
5784 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion76de5cf2005-11-18 18:25:12 +00005785 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005786 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
5787 break;
cerion32aad402005-09-10 12:02:24 +00005788
5789 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion76de5cf2005-11-18 18:25:12 +00005790 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005791 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
5792 break;
cerion32aad402005-09-10 12:02:24 +00005793
5794 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion76de5cf2005-11-18 18:25:12 +00005795 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00005796 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
5797 break;
cerion32aad402005-09-10 12:02:24 +00005798
5799 default:
5800 vex_printf("dis_av_cmp(PPC32)(opc2)\n");
5801 return False;
5802 }
cerion0c439222005-09-15 14:22:58 +00005803
5804 putVReg( vD_addr, mkexpr(vD) );
5805
cerion76de5cf2005-11-18 18:25:12 +00005806 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00005807 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00005808 }
cerion32aad402005-09-10 12:02:24 +00005809 return True;
5810}
5811
5812/*
5813 AltiVec Multiply-Sum Instructions
5814*/
5815static Bool dis_av_multarith ( UInt theInstr )
5816{
cerion76de5cf2005-11-18 18:25:12 +00005817 /* VA-Form */
5818 UChar opc1 = ifieldOPC(theInstr);
5819 UChar vD_addr = ifieldRegDS(theInstr);
5820 UChar vA_addr = ifieldRegA(theInstr);
5821 UChar vB_addr = ifieldRegB(theInstr);
5822 UChar vC_addr = ifieldRegC(theInstr);
5823 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00005824
cerion4a49b032005-11-08 16:23:07 +00005825 IRTemp vA = newTemp(Ity_V128);
5826 IRTemp vB = newTemp(Ity_V128);
5827 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00005828 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00005829 IRTemp aLo = newTemp(Ity_V128);
5830 IRTemp bLo = newTemp(Ity_V128);
5831 IRTemp cLo = newTemp(Ity_V128);
5832 IRTemp zLo = newTemp(Ity_V128);
5833 IRTemp aHi = newTemp(Ity_V128);
5834 IRTemp bHi = newTemp(Ity_V128);
5835 IRTemp cHi = newTemp(Ity_V128);
5836 IRTemp zHi = newTemp(Ity_V128);
5837 IRTemp abEvn = newTemp(Ity_V128);
5838 IRTemp abOdd = newTemp(Ity_V128);
5839 IRTemp z3 = newTemp(Ity_I64);
5840 IRTemp z2 = newTemp(Ity_I64);
5841 IRTemp z1 = newTemp(Ity_I64);
5842 IRTemp z0 = newTemp(Ity_I64);
5843 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
5844 IRTemp c3, c2, c1, c0;
5845
5846 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
5847 c3 = c2 = c1 = c0 = IRTemp_INVALID;
5848
cerion6f1cc0f2005-09-16 16:02:11 +00005849 assign( vA, getVReg(vA_addr));
5850 assign( vB, getVReg(vB_addr));
5851 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00005852 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00005853
cerion32aad402005-09-10 12:02:24 +00005854 if (opc1 != 0x4) {
5855 vex_printf("dis_av_multarith(PPC32)(instr)\n");
5856 return False;
5857 }
5858
5859 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00005860 /* Multiply-Add */
cerion6f1cc0f2005-09-16 16:02:11 +00005861 case 0x20: { // vmhaddshs (Multiply High, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00005862 IRTemp cSigns = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00005863 DIP("vmhaddshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion6f1cc0f2005-09-16 16:02:11 +00005864 assign( cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
5865 assign( aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)) );
5866 assign( bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)) );
5867 assign( cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns), mkexpr(vC)) );
5868 assign( aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)) );
5869 assign( bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)) );
5870 assign( cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns), mkexpr(vC)) );
cerion32aad402005-09-10 12:02:24 +00005871
cerion24d06f12005-11-09 21:34:20 +00005872 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00005873 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00005874 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00005875 mkexpr(aLo), mkexpr(bLo)),
5876 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005877
cerion24d06f12005-11-09 21:34:20 +00005878 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00005879 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00005880 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00005881 mkexpr(aHi), mkexpr(bHi)),
5882 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005883
5884 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
5885 break;
5886 }
5887 case 0x21: { // vmhraddshs (Multiply High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00005888 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00005889 IRTemp cSigns = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00005890 DIP("vmhraddshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion6f1cc0f2005-09-16 16:02:11 +00005891 assign( cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
5892 assign( aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)) );
5893 assign( bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)) );
5894 assign( cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns), mkexpr(vC)) );
5895 assign( aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)) );
5896 assign( bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)) );
5897 assign( cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns), mkexpr(vC)) );
cerion32aad402005-09-10 12:02:24 +00005898
cerion6f1cc0f2005-09-16 16:02:11 +00005899 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00005900 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
5901 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00005902
cerion24d06f12005-11-09 21:34:20 +00005903 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00005904 binop(Iop_SarN32x4,
5905 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00005906 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00005907 mkexpr(aLo), mkexpr(bLo))),
5908 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005909
cerion24d06f12005-11-09 21:34:20 +00005910 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00005911 binop(Iop_SarN32x4,
5912 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00005913 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00005914 mkexpr(aHi), mkexpr(bHi))),
5915 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005916
5917 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
5918 break;
5919 }
5920 case 0x22: { // vmladduhm (Multiply Low, Add Unsigned HW Modulo, AV p194)
sewardj197bd172005-10-12 11:34:33 +00005921 DIP("vmladduhm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion6f1cc0f2005-09-16 16:02:11 +00005922 assign( aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)) );
5923 assign( bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)) );
5924 assign( cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC)) );
5925 assign( aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)) );
5926 assign( bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)) );
5927 assign( cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC)) );
5928 assign( zLo, binop(Iop_Add32x4,
cerion24d06f12005-11-09 21:34:20 +00005929 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo) ),
cerion6f1cc0f2005-09-16 16:02:11 +00005930 mkexpr(cLo)) );
5931 assign( zHi, binop(Iop_Add32x4,
cerion24d06f12005-11-09 21:34:20 +00005932 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi) ),
cerion6f1cc0f2005-09-16 16:02:11 +00005933 mkexpr(cHi)) );
sewardj1bee5612005-11-10 18:10:58 +00005934 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00005935 break;
5936 }
cerion32aad402005-09-10 12:02:24 +00005937
5938
5939 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00005940 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00005941 IRTemp abEE, abEO, abOE, abOO;
5942 abEE = abEO = abOE = abOO = IRTemp_INVALID;
sewardj197bd172005-10-12 11:34:33 +00005943 DIP("vmsumubm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00005944
cerion4a49b032005-11-08 16:23:07 +00005945 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00005946 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
5947 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00005948
5949 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
5950 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
5951 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
5952
cerion6f1cc0f2005-09-16 16:02:11 +00005953 putVReg( vD_addr,
5954 binop(Iop_Add32x4, mkexpr(vC),
5955 binop(Iop_Add32x4,
cerion4a49b032005-11-08 16:23:07 +00005956 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
5957 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005958 break;
5959 }
cerion4a49b032005-11-08 16:23:07 +00005960 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
5961 IRTemp aEvn, aOdd, bEvn, bOdd;
5962 IRTemp abEE = newTemp(Ity_V128);
5963 IRTemp abEO = newTemp(Ity_V128);
5964 IRTemp abOE = newTemp(Ity_V128);
5965 IRTemp abOO = newTemp(Ity_V128);
5966 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00005967 DIP("vmsummbm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00005968
cerion4a49b032005-11-08 16:23:07 +00005969 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
5970 (separating out adjacent lanes to different vectors) */
5971 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
5972 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
5973
5974 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00005975 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
5976 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
5977 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
5978 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00005979
5980 /* add results together, + vC */
5981 putVReg( vD_addr,
5982 binop(Iop_QAdd32Sx4, mkexpr(vC),
5983 binop(Iop_QAdd32Sx4,
5984 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
cerion24d06f12005-11-09 21:34:20 +00005985 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00005986 break;
5987 }
cerion6f1cc0f2005-09-16 16:02:11 +00005988 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
sewardj197bd172005-10-12 11:34:33 +00005989 DIP("vmsumuhm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00005990 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
5991 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00005992 putVReg( vD_addr,
5993 binop(Iop_Add32x4, mkexpr(vC),
cerion4a49b032005-11-08 16:23:07 +00005994 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00005995 break;
5996 }
cerion4a49b032005-11-08 16:23:07 +00005997 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion32aad402005-09-10 12:02:24 +00005998 DIP("vmsumuhs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00005999 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00006000 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
6001 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00006002
cerion4a49b032005-11-08 16:23:07 +00006003 /* break V128 to 4xI32's, zero-extending to I64's */
6004 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
6005 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
6006 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
6007
6008 /* add lanes */
6009 assign( z3, binop(Iop_Add64, mkexpr(c3),
6010 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
6011 assign( z2, binop(Iop_Add64, mkexpr(c2),
6012 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
6013 assign( z1, binop(Iop_Add64, mkexpr(c1),
6014 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
6015 assign( z0, binop(Iop_Add64, mkexpr(c0),
6016 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
6017
6018 /* saturate-narrow to 32bit, and combine to V128 */
6019 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
6020 mkexpr(z1), mkexpr(z0)) );
6021
cerion6f1cc0f2005-09-16 16:02:11 +00006022 break;
6023 }
cerion4a49b032005-11-08 16:23:07 +00006024 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
6025 DIP("vmsumshm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00006026 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
6027 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00006028 putVReg( vD_addr,
6029 binop(Iop_Add32x4, mkexpr(vC),
6030 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
6031 break;
6032 }
6033 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion32aad402005-09-10 12:02:24 +00006034 DIP("vmsumshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00006035 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00006036 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
6037 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00006038
cerion4a49b032005-11-08 16:23:07 +00006039 /* break V128 to 4xI32's, sign-extending to I64's */
6040 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
6041 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
6042 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
6043
6044 /* add lanes */
6045 assign( z3, binop(Iop_Add64, mkexpr(c3),
6046 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
6047 assign( z2, binop(Iop_Add64, mkexpr(c2),
6048 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
6049 assign( z1, binop(Iop_Add64, mkexpr(c1),
6050 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
6051 assign( z0, binop(Iop_Add64, mkexpr(c0),
6052 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
6053
6054 /* saturate-narrow to 32bit, and combine to V128 */
6055 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
6056 mkexpr(z1), mkexpr(z0)) );
6057 break;
6058 }
cerion32aad402005-09-10 12:02:24 +00006059 default:
6060 vex_printf("dis_av_multarith(PPC32)(opc2)\n");
6061 return False;
6062 }
6063 return True;
6064}
6065
6066/*
6067 AltiVec Shift/Rotate Instructions
6068*/
6069static Bool dis_av_shift ( UInt theInstr )
6070{
cerion76de5cf2005-11-18 18:25:12 +00006071 /* VX-Form */
6072 UChar opc1 = ifieldOPC(theInstr);
6073 UChar vD_addr = ifieldRegDS(theInstr);
6074 UChar vA_addr = ifieldRegA(theInstr);
6075 UChar vB_addr = ifieldRegB(theInstr);
6076 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006077
cerion27b3d7e2005-09-14 20:35:47 +00006078 IRTemp vA = newTemp(Ity_V128);
6079 IRTemp vB = newTemp(Ity_V128);
6080 assign( vA, getVReg(vA_addr));
6081 assign( vB, getVReg(vB_addr));
6082
cerion32aad402005-09-10 12:02:24 +00006083 if (opc1 != 0x4){
6084 vex_printf("dis_av_shift(PPC32)(instr)\n");
6085 return False;
6086 }
6087
6088 switch (opc2) {
6089 /* Rotate */
6090 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
6091 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00006092 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00006093 break;
cerion32aad402005-09-10 12:02:24 +00006094
6095 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
6096 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00006097 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00006098 break;
cerion32aad402005-09-10 12:02:24 +00006099
6100 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
6101 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00006102 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00006103 break;
cerion32aad402005-09-10 12:02:24 +00006104
6105
6106 /* Shift Left */
6107 case 0x104: // vslb (Shift Left Integer B, AV p240)
6108 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006109 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
6110 break;
cerion32aad402005-09-10 12:02:24 +00006111
6112 case 0x144: // vslh (Shift Left Integer HW, AV p242)
6113 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006114 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
6115 break;
cerion32aad402005-09-10 12:02:24 +00006116
6117 case 0x184: // vslw (Shift Left Integer W, AV p244)
6118 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006119 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
6120 break;
cerion32aad402005-09-10 12:02:24 +00006121
cerion0a7b4f42005-09-16 07:54:40 +00006122 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00006123 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00006124 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006125 assign( sh, binop(Iop_And8, mkU8(0x7),
6126 unop(Iop_32to8,
6127 unop(Iop_V128to32, mkexpr(vB)))) );
6128 putVReg( vD_addr,
6129 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
6130 break;
6131 }
6132 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00006133 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00006134 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006135 assign( sh, binop(Iop_And8, mkU8(0x78),
6136 unop(Iop_32to8,
6137 unop(Iop_V128to32, mkexpr(vB)))) );
6138 putVReg( vD_addr,
6139 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
6140 break;
6141 }
6142
cerion32aad402005-09-10 12:02:24 +00006143
6144 /* Shift Right */
6145 case 0x204: // vsrb (Shift Right B, AV p256)
6146 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006147 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
6148 break;
cerion32aad402005-09-10 12:02:24 +00006149
6150 case 0x244: // vsrh (Shift Right HW, AV p257)
6151 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006152 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
6153 break;
cerion32aad402005-09-10 12:02:24 +00006154
6155 case 0x284: // vsrw (Shift Right W, AV p259)
6156 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006157 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
6158 break;
cerion32aad402005-09-10 12:02:24 +00006159
cerion27b3d7e2005-09-14 20:35:47 +00006160 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00006161 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00006162 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00006163 assign( sh, binop(Iop_And8, mkU8(0x7),
6164 unop(Iop_32to8,
6165 unop(Iop_V128to32, mkexpr(vB)))) );
6166 putVReg( vD_addr,
6167 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
6168 break;
6169 }
cerion32aad402005-09-10 12:02:24 +00006170 case 0x304: // vsrab (Shift Right Algebraic B, AV p253)
6171 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006172 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
6173 break;
cerion32aad402005-09-10 12:02:24 +00006174
6175 case 0x344: // vsrah (Shift Right Algebraic HW, AV p254)
6176 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006177 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
6178 break;
cerion32aad402005-09-10 12:02:24 +00006179
6180 case 0x384: // vsraw (Shift Right Algebraic W, AV p255)
6181 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006182 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
6183 break;
cerion32aad402005-09-10 12:02:24 +00006184
cerion0a7b4f42005-09-16 07:54:40 +00006185 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00006186 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00006187 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00006188 assign( sh, binop(Iop_And8, mkU8(0x78),
6189 unop(Iop_32to8,
6190 unop(Iop_V128to32, mkexpr(vB)))) );
6191 putVReg( vD_addr,
6192 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
6193 break;
6194 }
cerion32aad402005-09-10 12:02:24 +00006195
6196 default:
6197 vex_printf("dis_av_shift(PPC32)(opc2)\n");
6198 return False;
6199 }
6200 return True;
6201}
6202
6203/*
6204 AltiVec Permute Instructions
6205*/
6206static Bool dis_av_permute ( UInt theInstr )
6207{
cerion76de5cf2005-11-18 18:25:12 +00006208 /* VA-Form, VX-Form */
6209 UChar opc1 = ifieldOPC(theInstr);
6210 UChar vD_addr = ifieldRegDS(theInstr);
6211 UChar vA_addr = ifieldRegA(theInstr);
6212 UChar UIMM_5 = vA_addr;
6213 UChar vB_addr = ifieldRegB(theInstr);
6214 UChar vC_addr = ifieldRegC(theInstr);
6215 UChar b10 = ifieldBIT10(theInstr);
6216 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
6217 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00006218
cerion76de5cf2005-11-18 18:25:12 +00006219 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00006220
cerion6e7a0ea2005-09-13 13:34:09 +00006221 IRTemp vA = newTemp(Ity_V128);
6222 IRTemp vB = newTemp(Ity_V128);
6223 IRTemp vC = newTemp(Ity_V128);
6224 assign( vA, getVReg(vA_addr));
6225 assign( vB, getVReg(vB_addr));
6226 assign( vC, getVReg(vC_addr));
6227
cerion32aad402005-09-10 12:02:24 +00006228 if (opc1 != 0x4) {
6229 vex_printf("dis_av_permute(PPC32)(instr)\n");
6230 return False;
6231 }
6232
6233 switch (opc2) {
6234 case 0x2A: // vsel (Conditional Select, AV p238)
6235 DIP("vsel v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00006236 /* vD = (vA & ~vC) | (vB & vC) */
6237 putVReg( vD_addr, binop(Iop_OrV128,
6238 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
6239 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
6240 return True;
cerion32aad402005-09-10 12:02:24 +00006241
cerion92d9d872005-09-15 21:58:50 +00006242 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00006243 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00006244 IRTemp a_perm = newTemp(Ity_V128);
6245 IRTemp b_perm = newTemp(Ity_V128);
6246 IRTemp mask = newTemp(Ity_V128);
6247 IRTemp vC_andF = newTemp(Ity_V128);
6248 DIP("vperm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
6249 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
6250 IR specifies, and also to hide irrelevant bits from
6251 memcheck */
6252 assign( vC_andF, binop(Iop_AndV128, mkexpr(vC),
6253 unop(Iop_Dup8x16, mkU8(0xF))) );
6254 assign( a_perm, binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
6255 assign( b_perm, binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00006256 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
6257 assign( mask, binop(Iop_SarN8x16,
6258 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
6259 mkU8(7)) );
6260 // dst = (a & ~mask) | (b & mask)
6261 putVReg( vD_addr, binop(Iop_OrV128,
6262 binop(Iop_AndV128, mkexpr(a_perm),
6263 unop(Iop_NotV128, mkexpr(mask))),
6264 binop(Iop_AndV128, mkexpr(b_perm),
6265 mkexpr(mask))) );
6266 return True;
6267 }
cerion32aad402005-09-10 12:02:24 +00006268 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
6269 if (b10 != 0) {
6270 vex_printf("dis_av_permute(PPC32)(vsldoi)\n");
6271 return False;
6272 }
6273 DIP("vsldoi v%d,v%d,v%d,%d\n", vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00006274 if (SHB_uimm4 == 0)
6275 putVReg( vD_addr, mkexpr(vA) );
6276 else
6277 putVReg( vD_addr,
6278 binop(Iop_OrV128,
6279 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
6280 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
6281 return True;
cerion32aad402005-09-10 12:02:24 +00006282
6283 default:
6284 break; // Fall through...
6285 }
6286
cerion76de5cf2005-11-18 18:25:12 +00006287 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006288 switch (opc2) {
6289
6290 /* Merge */
6291 case 0x00C: // vmrghb (Merge High B, AV p195)
6292 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006293 putVReg( vD_addr,
6294 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
6295 break;
cerion32aad402005-09-10 12:02:24 +00006296
6297 case 0x04C: // vmrghh (Merge High HW, AV p196)
6298 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006299 putVReg( vD_addr,
6300 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
6301 break;
cerion32aad402005-09-10 12:02:24 +00006302
6303 case 0x08C: // vmrghw (Merge High W, AV p197)
6304 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006305 putVReg( vD_addr,
6306 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
6307 break;
cerion32aad402005-09-10 12:02:24 +00006308
6309 case 0x10C: // vmrglb (Merge Low B, AV p198)
6310 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006311 putVReg( vD_addr,
6312 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
6313 break;
cerion32aad402005-09-10 12:02:24 +00006314
6315 case 0x14C: // vmrglh (Merge Low HW, AV p199)
6316 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006317 putVReg( vD_addr,
6318 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
6319 break;
cerion32aad402005-09-10 12:02:24 +00006320
6321 case 0x18C: // vmrglw (Merge Low W, AV p200)
6322 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00006323 putVReg( vD_addr,
6324 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
6325 break;
6326
cerion32aad402005-09-10 12:02:24 +00006327
6328 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00006329 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00006330 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00006331 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00006332 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00006333 putVReg( vD_addr, unop(Iop_Dup8x16,
6334 unop(Iop_32to8, unop(Iop_V128to32,
6335 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
6336 break;
6337 }
6338 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00006339 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00006340 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00006341 putVReg( vD_addr, unop(Iop_Dup16x8,
6342 unop(Iop_32to16, unop(Iop_V128to32,
6343 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
6344 break;
6345 }
cerion27b3d7e2005-09-14 20:35:47 +00006346 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00006347 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00006348 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00006349 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00006350 putVReg( vD_addr, unop(Iop_Dup32x4,
6351 unop(Iop_V128to32,
6352 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
6353 break;
6354 }
cerion32aad402005-09-10 12:02:24 +00006355 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
6356 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00006357 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
6358 break;
cerion32aad402005-09-10 12:02:24 +00006359
6360 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
6361 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00006362 putVReg( vD_addr, unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
6363 break;
cerion32aad402005-09-10 12:02:24 +00006364
6365 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
6366 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00006367 putVReg( vD_addr, unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
6368 break;
cerion32aad402005-09-10 12:02:24 +00006369
6370 default:
6371 vex_printf("dis_av_permute(PPC32)(opc2)\n");
6372 return False;
6373 }
6374 return True;
6375}
6376
6377/*
6378 AltiVec Pack/Unpack Instructions
6379*/
6380static Bool dis_av_pack ( UInt theInstr )
6381{
cerion76de5cf2005-11-18 18:25:12 +00006382 /* VX-Form */
6383 UChar opc1 = ifieldOPC(theInstr);
6384 UChar vD_addr = ifieldRegDS(theInstr);
6385 UChar vA_addr = ifieldRegA(theInstr);
6386 UChar vB_addr = ifieldRegB(theInstr);
6387 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006388
sewardj197bd172005-10-12 11:34:33 +00006389 IRTemp signs = IRTemp_INVALID;
6390 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00006391 IRTemp vA = newTemp(Ity_V128);
6392 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00006393 assign( vA, getVReg(vA_addr));
6394 assign( vB, getVReg(vB_addr));
6395
cerion32aad402005-09-10 12:02:24 +00006396 if (opc1 != 0x4) {
6397 vex_printf("dis_av_pack(PPC32)(instr)\n");
6398 return False;
6399 }
6400
6401 switch (opc2) {
6402 /* Packing */
6403 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
6404 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00006405 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00006406 return True;
cerion32aad402005-09-10 12:02:24 +00006407
6408 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
6409 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00006410 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00006411 return True;
cerion32aad402005-09-10 12:02:24 +00006412
6413 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
6414 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006415 putVReg( vD_addr, binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
6416 // TODO: set VSCR[SAT]
6417 return True;
cerion32aad402005-09-10 12:02:24 +00006418
6419 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
6420 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006421 putVReg( vD_addr, binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
6422 // TODO: set VSCR[SAT]
6423 return True;
cerion32aad402005-09-10 12:02:24 +00006424
cerion3c052792005-09-16 07:13:44 +00006425 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00006426 // This insn does a signed->unsigned saturating conversion.
6427 // Conversion done here, then uses unsigned->unsigned vpk insn:
6428 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
6429 IRTemp vA_tmp = newTemp(Ity_V128);
6430 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00006431 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006432 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
6433 unop(Iop_NotV128,
6434 binop(Iop_SarN16x8,
6435 mkexpr(vA), mkU8(15)))) );
6436 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
6437 unop(Iop_NotV128,
6438 binop(Iop_SarN16x8,
6439 mkexpr(vB), mkU8(15)))) );
6440 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
6441 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
6442 // TODO: set VSCR[SAT]
6443 return True;
6444 }
6445 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00006446 // This insn does a signed->unsigned saturating conversion.
6447 // Conversion done here, then uses unsigned->unsigned vpk insn:
6448 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
6449 IRTemp vA_tmp = newTemp(Ity_V128);
6450 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00006451 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006452 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
6453 unop(Iop_NotV128,
6454 binop(Iop_SarN32x4,
6455 mkexpr(vA), mkU8(31)))) );
6456 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
6457 unop(Iop_NotV128,
6458 binop(Iop_SarN32x4,
6459 mkexpr(vB), mkU8(31)))) );
6460 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
6461 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
6462 // TODO: set VSCR[SAT]
6463 return True;
6464 }
cerion32aad402005-09-10 12:02:24 +00006465 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
6466 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006467 putVReg( vD_addr, binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
6468 // TODO: set VSCR[SAT]
6469 return True;
cerion32aad402005-09-10 12:02:24 +00006470
6471 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
6472 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006473 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
6474 // TODO: set VSCR[SAT]
6475 return True;
cerion32aad402005-09-10 12:02:24 +00006476
cerion3c052792005-09-16 07:13:44 +00006477 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00006478 /* CAB: Worth a new primop? */
6479 /* Using shifts to compact pixel elements, then packing them them */
6480 IRTemp a1 = newTemp(Ity_V128);
6481 IRTemp a2 = newTemp(Ity_V128);
6482 IRTemp a3 = newTemp(Ity_V128);
6483 IRTemp a_tmp = newTemp(Ity_V128);
6484 IRTemp b1 = newTemp(Ity_V128);
6485 IRTemp b2 = newTemp(Ity_V128);
6486 IRTemp b3 = newTemp(Ity_V128);
6487 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00006488 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006489 assign( a1, binop(Iop_ShlN16x8,
6490 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
6491 mkU8(10)) );
6492 assign( a2, binop(Iop_ShlN16x8,
6493 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
6494 mkU8(5)) );
6495 assign( a3, binop(Iop_ShrN16x8,
6496 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
6497 mkU8(11)) );
6498 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
6499 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
6500
6501 assign( b1, binop(Iop_ShlN16x8,
6502 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
6503 mkU8(10)) );
6504 assign( b2, binop(Iop_ShlN16x8,
6505 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
6506 mkU8(5)) );
6507 assign( b3, binop(Iop_ShrN16x8,
6508 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
6509 mkU8(11)) );
6510 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
6511 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
6512
sewardj1bee5612005-11-10 18:10:58 +00006513 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00006514 mkexpr(a_tmp), mkexpr(b_tmp)) );
6515 return True;
6516 }
cerion32aad402005-09-10 12:02:24 +00006517
6518 default:
6519 break; // Fall through...
6520 }
6521
6522
6523 if (vA_addr != 0) {
6524 vex_printf("dis_av_pack(PPC32)(vA_addr)\n");
6525 return False;
6526 }
6527
sewardj197bd172005-10-12 11:34:33 +00006528 signs = newTemp(Ity_V128);
6529 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00006530 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
6531
cerion32aad402005-09-10 12:02:24 +00006532 switch (opc2) {
6533 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00006534 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00006535 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006536 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
6537 putVReg( vD_addr, binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
6538 break;
6539 }
6540 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00006541 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006542 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
6543 putVReg( vD_addr, binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
6544 break;
6545 }
6546 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00006547 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006548 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
6549 putVReg( vD_addr, binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
6550 break;
6551 }
6552 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00006553 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006554 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
6555 putVReg( vD_addr, binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
6556 break;
6557 }
6558 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00006559 /* CAB: Worth a new primop? */
6560 /* Using shifts to isolate pixel elements, then expanding them */
6561 IRTemp z0 = newTemp(Ity_V128);
6562 IRTemp z1 = newTemp(Ity_V128);
6563 IRTemp z01 = newTemp(Ity_V128);
6564 IRTemp z2 = newTemp(Ity_V128);
6565 IRTemp z3 = newTemp(Ity_V128);
6566 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00006567 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006568 assign( z0, binop(Iop_ShlN16x8,
6569 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
6570 mkU8(8)) );
6571 assign( z1, binop(Iop_ShrN16x8,
6572 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
6573 mkU8(11)) );
6574 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
6575 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
6576 assign( z2, binop(Iop_ShrN16x8,
6577 binop(Iop_ShlN16x8,
6578 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
6579 mkU8(11)),
6580 mkU8(3)) );
6581 assign( z3, binop(Iop_ShrN16x8,
6582 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
6583 mkU8(11)) );
6584 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
6585 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
6586 putVReg( vD_addr, binop(Iop_OrV128,
6587 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
6588 mkexpr(z23)) );
6589 break;
6590 }
6591 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00006592 /* identical to vupkhpx, except interleaving LO */
6593 IRTemp z0 = newTemp(Ity_V128);
6594 IRTemp z1 = newTemp(Ity_V128);
6595 IRTemp z01 = newTemp(Ity_V128);
6596 IRTemp z2 = newTemp(Ity_V128);
6597 IRTemp z3 = newTemp(Ity_V128);
6598 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00006599 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00006600 assign( z0, binop(Iop_ShlN16x8,
6601 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
6602 mkU8(8)) );
6603 assign( z1, binop(Iop_ShrN16x8,
6604 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
6605 mkU8(11)) );
6606 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
6607 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
6608 assign( z2, binop(Iop_ShrN16x8,
6609 binop(Iop_ShlN16x8,
6610 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
6611 mkU8(11)),
6612 mkU8(3)) );
6613 assign( z3, binop(Iop_ShrN16x8,
6614 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
6615 mkU8(11)) );
6616 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
6617 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
6618 putVReg( vD_addr, binop(Iop_OrV128,
6619 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
6620 mkexpr(z23)) );
6621 break;
6622 }
cerion32aad402005-09-10 12:02:24 +00006623 default:
6624 vex_printf("dis_av_pack(PPC32)(opc2)\n");
6625 return False;
6626 }
6627 return True;
6628}
6629
6630
6631/*
6632 AltiVec Floating Point Arithmetic Instructions
6633*/
6634static Bool dis_av_fp_arith ( UInt theInstr )
6635{
cerion76de5cf2005-11-18 18:25:12 +00006636 /* VA-Form */
6637 UChar opc1 = ifieldOPC(theInstr);
6638 UChar vD_addr = ifieldRegDS(theInstr);
6639 UChar vA_addr = ifieldRegA(theInstr);
6640 UChar vB_addr = ifieldRegB(theInstr);
6641 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00006642 UInt opc2=0;
6643
cerion8ea0d3e2005-11-14 00:44:47 +00006644 IRTemp vA = newTemp(Ity_V128);
6645 IRTemp vB = newTemp(Ity_V128);
6646 IRTemp vC = newTemp(Ity_V128);
6647 assign( vA, getVReg(vA_addr));
6648 assign( vB, getVReg(vB_addr));
6649 assign( vC, getVReg(vC_addr));
6650
cerion32aad402005-09-10 12:02:24 +00006651 if (opc1 != 0x4) {
6652 vex_printf("dis_av_fp_arith(PPC32)(instr)\n");
6653 return False;
6654 }
6655
cerion76de5cf2005-11-18 18:25:12 +00006656 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00006657 switch (opc2) {
6658 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
6659 DIP("vmaddfp v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vC_addr, vB_addr);
cerionf3f173c2005-11-14 02:37:44 +00006660 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vB),
6661 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
6662 return True;
cerion32aad402005-09-10 12:02:24 +00006663
cerionf3f173c2005-11-14 02:37:44 +00006664 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion32aad402005-09-10 12:02:24 +00006665 DIP("vnmsubfp v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vC_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00006666 putVReg( vD_addr, binop(Iop_Sub32Fx4,
6667 mkexpr(vB),
6668 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00006669 return True;
6670 }
cerion32aad402005-09-10 12:02:24 +00006671
6672 default:
6673 break; // Fall through...
6674 }
6675
cerion76de5cf2005-11-18 18:25:12 +00006676 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006677 switch (opc2) {
6678 case 0x00A: // vaddfp (Add FP, AV p137)
6679 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006680 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
6681 return True;
cerion32aad402005-09-10 12:02:24 +00006682
6683 case 0x04A: // vsubfp (Subtract FP, AV p261)
6684 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006685 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
6686 return True;
cerion32aad402005-09-10 12:02:24 +00006687
6688 case 0x40A: // vmaxfp (Maximum FP, AV p178)
6689 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006690 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
6691 return True;
cerion32aad402005-09-10 12:02:24 +00006692
6693 case 0x44A: // vminfp (Minimum FP, AV p187)
6694 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006695 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
6696 return True;
cerion32aad402005-09-10 12:02:24 +00006697
6698 default:
6699 break; // Fall through...
6700 }
6701
6702
6703 if (vA_addr != 0) {
6704 vex_printf("dis_av_fp_arith(PPC32)(vA_addr)\n");
6705 return False;
6706 }
6707
6708 switch (opc2) {
6709 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
6710 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006711 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
6712 return True;
cerion32aad402005-09-10 12:02:24 +00006713
6714 case 0x14A: // vrsqrtefp (Reciprocal Square Root Estimate FP, AV p237)
6715 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006716 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
6717 return True;
cerion32aad402005-09-10 12:02:24 +00006718
6719 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
6720 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
6721 DIP(" => not implemented\n");
6722 return False;
6723
6724 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
6725 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
6726 DIP(" => not implemented\n");
6727 return False;
6728
6729 default:
6730 vex_printf("dis_av_fp_arith(PPC32)(opc2=0x%x)\n",opc2);
6731 return False;
6732 }
6733 return True;
6734}
6735
6736/*
6737 AltiVec Floating Point Compare Instructions
6738*/
6739static Bool dis_av_fp_cmp ( UInt theInstr )
6740{
cerion76de5cf2005-11-18 18:25:12 +00006741 /* VXR-Form */
6742 UChar opc1 = ifieldOPC(theInstr);
6743 UChar vD_addr = ifieldRegDS(theInstr);
6744 UChar vA_addr = ifieldRegA(theInstr);
6745 UChar vB_addr = ifieldRegB(theInstr);
6746 UChar flag_rC = ifieldBIT10(theInstr);
6747 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00006748
cerion8ea0d3e2005-11-14 00:44:47 +00006749 Bool cmp_bounds = False;
6750
6751 IRTemp vA = newTemp(Ity_V128);
6752 IRTemp vB = newTemp(Ity_V128);
6753 IRTemp vD = newTemp(Ity_V128);
6754 assign( vA, getVReg(vA_addr));
6755 assign( vB, getVReg(vB_addr));
6756
cerion32aad402005-09-10 12:02:24 +00006757 if (opc1 != 0x4) {
6758 vex_printf("dis_av_fp_cmp(PPC32)(instr)\n");
6759 return False;
6760 }
6761
6762 switch (opc2) {
6763 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion76de5cf2005-11-18 18:25:12 +00006764 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006765 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
6766 break;
cerion32aad402005-09-10 12:02:24 +00006767
6768 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to FP, AV p163)
cerion76de5cf2005-11-18 18:25:12 +00006769 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006770 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
6771 break;
cerion32aad402005-09-10 12:02:24 +00006772
6773 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion76de5cf2005-11-18 18:25:12 +00006774 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006775 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
6776 break;
cerion32aad402005-09-10 12:02:24 +00006777
cerion8ea0d3e2005-11-14 00:44:47 +00006778 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
6779 IRTemp gt = newTemp(Ity_V128);
6780 IRTemp lt = newTemp(Ity_V128);
6781 IRTemp zeros = newTemp(Ity_V128);
cerion76de5cf2005-11-18 18:25:12 +00006782 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00006783 cmp_bounds = True;
6784 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
6785
6786 /* Note: making use of fact that the ppc backend for compare insns
6787 return zero'd lanes if either of the corresponding arg lanes is a nan.
6788
6789 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
6790 need this for the other compares too (vcmpeqfp etc)...
6791 Better still, tighten down the spec for compare irops.
6792 */
6793 assign( gt, unop(Iop_NotV128,
6794 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
6795 assign( lt, unop(Iop_NotV128,
6796 binop(Iop_CmpGE32Fx4, mkexpr(vA),
6797 binop(Iop_Sub32Fx4, mkexpr(zeros), mkexpr(vB)))) );
6798
6799 // finally, just shift gt,lt to correct position
6800 assign( vD, binop(Iop_ShlN32x4,
6801 binop(Iop_OrV128,
6802 binop(Iop_AndV128, mkexpr(gt),
6803 unop(Iop_Dup32x4, mkU32(0x2))),
6804 binop(Iop_AndV128, mkexpr(lt),
6805 unop(Iop_Dup32x4, mkU32(0x1)))),
6806 mkU8(30)) );
6807 break;
6808 }
cerion32aad402005-09-10 12:02:24 +00006809
6810 default:
6811 vex_printf("dis_av_fp_cmp(PPC32)(opc2)\n");
6812 return False;
6813 }
cerion8ea0d3e2005-11-14 00:44:47 +00006814
6815 putVReg( vD_addr, mkexpr(vD) );
6816
cerion76de5cf2005-11-18 18:25:12 +00006817 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00006818 set_AV_CR6( mkexpr(vD), !cmp_bounds );
6819 }
cerion32aad402005-09-10 12:02:24 +00006820 return True;
6821}
6822
6823/*
6824 AltiVec Floating Point Convert/Round Instructions
6825*/
6826static Bool dis_av_fp_convert ( UInt theInstr )
6827{
cerion76de5cf2005-11-18 18:25:12 +00006828 /* VX-Form */
6829 UChar opc1 = ifieldOPC(theInstr);
6830 UChar vD_addr = ifieldRegDS(theInstr);
6831 UChar UIMM_5 = ifieldRegA(theInstr);
6832 UChar vB_addr = ifieldRegB(theInstr);
6833 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006834
cerion76de5cf2005-11-18 18:25:12 +00006835 IRTemp vB = newTemp(Ity_V128);
6836 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00006837 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00006838
6839 float scale, inv_scale;
6840
ceriond963eb42005-11-16 18:02:58 +00006841 assign( vB, getVReg(vB_addr));
6842
6843 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00006844 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00006845 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00006846 inv_scale = 1/scale;
sewardj2ead5222005-11-23 03:53:45 +00006847 assign( vInvScale, unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00006848
cerion32aad402005-09-10 12:02:24 +00006849 if (opc1 != 0x4) {
6850 vex_printf("dis_av_fp_convert(PPC32)(instr)\n");
6851 return False;
6852 }
6853
6854 switch (opc2) {
6855 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
6856 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00006857 putVReg( vD_addr, binop(Iop_Mul32Fx4,
6858 unop(Iop_I32UtoFx4, mkexpr(vB)),
6859 mkexpr(vInvScale)) );
6860 return True;
cerion32aad402005-09-10 12:02:24 +00006861
6862 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
6863 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00006864
6865 putVReg( vD_addr, binop(Iop_Mul32Fx4,
6866 unop(Iop_I32StoFx4, mkexpr(vB)),
6867 mkexpr(vInvScale)) );
6868 return True;
cerion32aad402005-09-10 12:02:24 +00006869
6870 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
6871 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00006872 putVReg( vD_addr,
6873 unop(Iop_QFtoI32Ux4_RZ,
6874 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
6875 return True;
cerion32aad402005-09-10 12:02:24 +00006876
6877 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
6878 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00006879 putVReg( vD_addr,
6880 unop(Iop_QFtoI32Sx4_RZ,
6881 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
6882 return True;
cerion32aad402005-09-10 12:02:24 +00006883
6884 default:
6885 break; // Fall through...
6886 }
6887
6888 if (UIMM_5 != 0) {
6889 vex_printf("dis_av_fp_convert(PPC32)(UIMM_5)\n");
6890 return False;
6891 }
6892
6893 switch (opc2) {
6894 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
6895 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00006896 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
6897 break;
cerion32aad402005-09-10 12:02:24 +00006898
6899 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
6900 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00006901 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
6902 break;
cerion32aad402005-09-10 12:02:24 +00006903
6904 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
6905 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00006906 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
6907 break;
cerion32aad402005-09-10 12:02:24 +00006908
6909 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
6910 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00006911 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
6912 break;
cerion32aad402005-09-10 12:02:24 +00006913
6914 default:
6915 vex_printf("dis_av_fp_convert(PPC32)(opc2)\n");
6916 return False;
6917 }
6918 return True;
6919}
cerion3d870a32005-03-18 12:23:33 +00006920
6921
cerion91ad5362005-01-27 23:02:41 +00006922
6923
6924
6925
cerion896a1372005-01-25 12:24:25 +00006926/*------------------------------------------------------------*/
6927/*--- Disassemble a single instruction ---*/
6928/*------------------------------------------------------------*/
6929
6930/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00006931 is located in host memory at &guest_code[delta]. */
6932
6933static
6934DisResult disInstr_PPC32_WRK (
6935 Bool put_IP,
6936 Bool (*resteerOkFn) ( Addr64 ),
6937 Long delta64,
6938 VexArchInfo* archinfo
6939 )
cerion896a1372005-01-25 12:24:25 +00006940{
sewardj9e6491a2005-07-02 19:24:10 +00006941 UChar opc1;
6942 UInt opc2;
6943 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00006944 UInt theInstr;
6945
sewardj059601a2005-11-13 00:53:05 +00006946 /* What insn variants are we supporting today? */
6947 Bool allow_FP = archinfo->subarch == VexSubArchPPC32_FI
6948 || archinfo->subarch == VexSubArchPPC32_VFI;
6949
6950 Bool allow_VMX = archinfo->subarch == VexSubArchPPC32_VFI;
6951
sewardj9e6491a2005-07-02 19:24:10 +00006952 /* The running delta */
6953 Int delta = (Int)delta64;
6954
6955 /* Set result defaults. */
6956 dres.whatNext = Dis_Continue;
6957 dres.len = 0;
6958 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00006959
cerion1515db92005-01-25 17:21:23 +00006960 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00006961 4-aligned. So just fish the whole thing out of memory right now
6962 and have done. */
cerioncf004462005-01-31 15:24:55 +00006963 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00006964
sewardjb51f0f42005-07-18 11:38:02 +00006965 DIP("\t0x%x: ", guest_CIA_curr_instr);
6966
6967 /* We may be asked to update the guest CIA before going further. */
6968 if (put_IP)
6969 putSPR( PPC32_SPR_CIA, mkU32(guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00006970
cerion896a1372005-01-25 12:24:25 +00006971 /* Spot the client-request magic sequence. */
6972 // Essentially a v. unlikely sequence of noops that we can catch
6973 {
sewardj2f52de42005-07-03 01:51:29 +00006974 UChar* code = (UChar*)(&guest_code[delta]);
cerion896a1372005-01-25 12:24:25 +00006975
6976 /* Spot this:
sewardj2f52de42005-07-03 01:51:29 +00006977 0x7C03D808 tw 0,3,27 => trap word if (0) => nop
cerionb85e8bb2005-02-16 08:54:33 +00006978 0x5400E800 rlwinm 0,0,29,0,0 => r0 = rotl(r0,29)
6979 0x54001800 rlwinm 0,0,3,0,0 => r0 = rotl(r0,3)
6980 0x54006800 rlwinm 0,0,13,0,0 => r0 = rotl(r0,13)
6981 0x54009800 rlwinm 0,0,19,0,0 => r0 = rotl(r0,19)
cerion0fe6b7e2005-06-20 16:28:32 +00006982 0x60000000 nop
cerion896a1372005-01-25 12:24:25 +00006983 */
sewardj2f52de42005-07-03 01:51:29 +00006984 if (getUIntBigendianly(code+ 0) == 0x7C03D808 &&
6985 getUIntBigendianly(code+ 4) == 0x5400E800 &&
6986 getUIntBigendianly(code+ 8) == 0x54001800 &&
6987 getUIntBigendianly(code+12) == 0x54006800 &&
6988 getUIntBigendianly(code+16) == 0x54009800 &&
6989 getUIntBigendianly(code+20) == 0x60000000) {
cerion84ad6162005-06-23 15:25:57 +00006990 DIP("%%r3 = client_request ( %%r31 )\n");
sewardj9e6491a2005-07-02 19:24:10 +00006991 dres.len = 24;
cerioned623db2005-06-20 12:42:04 +00006992 delta += 24;
6993
sewardj9e6491a2005-07-02 19:24:10 +00006994 irbb->next = mkU32(guest_CIA_bbstart+delta);
cerionb85e8bb2005-02-16 08:54:33 +00006995 irbb->jumpkind = Ijk_ClientReq;
sewardj9e6491a2005-07-02 19:24:10 +00006996 dres.whatNext = Dis_StopHere;
cerion896a1372005-01-25 12:24:25 +00006997 goto decode_success;
6998 }
6999 }
7000
cerion76de5cf2005-11-18 18:25:12 +00007001 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00007002 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00007003
cerion91ad5362005-01-27 23:02:41 +00007004 // Note: all 'reserved' bits must be cleared, else invalid
7005 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00007006
cerione9d361a2005-03-04 17:35:29 +00007007 /* Integer Arithmetic Instructions */
7008 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
7009 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
7010 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007011 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007012
cerione9d361a2005-03-04 17:35:29 +00007013 /* Integer Compare Instructions */
7014 case 0x0B: case 0x0A: // cmpi, cmpli
7015 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007016 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007017
cerione9d361a2005-03-04 17:35:29 +00007018 /* Integer Logical Instructions */
7019 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
7020 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
7021 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007022 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007023
cerione9d361a2005-03-04 17:35:29 +00007024 /* Integer Rotate Instructions */
7025 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
7026 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007027 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00007028
cerione9d361a2005-03-04 17:35:29 +00007029 /* Integer Load Instructions */
7030 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
7031 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
7032 case 0x20: case 0x21: // lwz, lwzu
7033 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007034 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00007035
cerione9d361a2005-03-04 17:35:29 +00007036 /* Integer Store Instructions */
7037 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
7038 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
7039 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007040 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00007041
sewardj7787af42005-08-04 18:32:19 +00007042 /* Integer Load and Store Multiple Instructions */
7043 case 0x2E: case 0x2F: // lmw, stmw
7044 if (dis_int_ldst_mult( theInstr )) goto decode_success;
7045 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00007046
cerione9d361a2005-03-04 17:35:29 +00007047 /* Branch Instructions */
7048 case 0x12: case 0x10: // b, bc
sewardj9d540e52005-10-08 11:28:16 +00007049 if (dis_branch(theInstr, &dres, resteerOkFn)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007050 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00007051
cerione9d361a2005-03-04 17:35:29 +00007052 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00007053 case 0x11: // sc
sewardj9e6491a2005-07-02 19:24:10 +00007054 if (dis_syslink(theInstr, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007055 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00007056
sewardjb51f0f42005-07-18 11:38:02 +00007057//zz /* Trap Instructions */
7058//zz case 0x03: // twi
7059//zz DIP("trap op (twi) => not implemented\n");
7060//zz goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00007061
cerion3d870a32005-03-18 12:23:33 +00007062 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00007063 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
7064 case 0x33: // lfdu
sewardj059601a2005-11-13 00:53:05 +00007065 if (!allow_FP) goto decode_failure;
cerion3d870a32005-03-18 12:23:33 +00007066 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00007067 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00007068
cerion3d870a32005-03-18 12:23:33 +00007069 /* Floating Point Store Instructions */
7070 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
7071 case 0x37: // stfdux
sewardj059601a2005-11-13 00:53:05 +00007072 if (!allow_FP) goto decode_failure;
cerion3d870a32005-03-18 12:23:33 +00007073 if (dis_fp_store( theInstr )) goto decode_success;
7074 goto decode_failure;
7075
sewardje14bb9f2005-07-22 09:39:02 +00007076 case 0x3B:
sewardj059601a2005-11-13 00:53:05 +00007077 if (!allow_FP) goto decode_failure;
cerion76de5cf2005-11-18 18:25:12 +00007078
7079 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00007080 switch (opc2) {
7081 /* Floating Point Arith Instructions */
7082 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
7083 case 0x16: case 0x18: case 0x19: // fsqrts, fres, fmuls
7084 if (dis_fp_arith(theInstr)) goto decode_success;
7085 goto decode_failure;
7086
7087 /* Floating Point Mult-Add Instructions */
7088 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
7089 case 0x1F: // fnmadds
7090 if (dis_fp_multadd(theInstr)) goto decode_success;
7091 goto decode_failure;
7092
7093 default:
7094 goto decode_failure;
7095 }
7096 break;
cerion3d870a32005-03-18 12:23:33 +00007097
7098 case 0x3F:
sewardj059601a2005-11-13 00:53:05 +00007099 if (!allow_FP) goto decode_failure;
cerion3d870a32005-03-18 12:23:33 +00007100 /* Instrs using opc[1:5] never overlap with instrs using opc[1:10],
7101 so we can simply fall through the first switch statement */
7102
cerion76de5cf2005-11-18 18:25:12 +00007103 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00007104 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00007105 /* Floating Point Arith Instructions */
7106 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
7107 case 0x16: case 0x17: case 0x19: // fsqrt, fsel, fmul
7108 case 0x1A: // frsqrte
7109 if (dis_fp_arith(theInstr)) goto decode_success;
7110 goto decode_failure;
cerion3d870a32005-03-18 12:23:33 +00007111
sewardje14bb9f2005-07-22 09:39:02 +00007112 /* Floating Point Mult-Add Instructions */
7113 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
7114 case 0x1F: // fnmadd
7115 if (dis_fp_multadd(theInstr)) goto decode_success;
7116 goto decode_failure;
7117
sewardjb51f0f42005-07-18 11:38:02 +00007118 default:
7119 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00007120 }
7121
cerion76de5cf2005-11-18 18:25:12 +00007122 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00007123 switch (opc2) {
7124 /* Floating Point Compare Instructions */
7125 case 0x000: // fcmpu
7126 case 0x020: // fcmpo
7127 if (dis_fp_cmp(theInstr)) goto decode_success;
7128 goto decode_failure;
7129
7130 /* Floating Point Rounding/Conversion Instructions */
7131 case 0x00C: // frsp
7132 case 0x00E: // fctiw
7133 case 0x00F: // fctiwz
7134 if (dis_fp_round(theInstr)) goto decode_success;
7135 goto decode_failure;
7136
7137 /* Floating Point Move Instructions */
7138 case 0x028: // fneg
7139 case 0x048: // fmr
7140 case 0x088: // fnabs
7141 case 0x108: // fabs
7142 if (dis_fp_move( theInstr )) goto decode_success;
7143 goto decode_failure;
7144
sewardjb51f0f42005-07-18 11:38:02 +00007145//zz /* Floating Point Status/Control Register Instructions */
7146//zz case 0x026: // mtfsb1
7147//zz case 0x040: // mcrfs
sewardje14bb9f2005-07-22 09:39:02 +00007148 case 0x046: // mtfsb0
7149 case 0x086: // mtfsfi
7150 case 0x247: // mffs
7151 case 0x2C7: // mtfsf
7152 if (dis_fp_scr( theInstr )) goto decode_success;
7153 goto decode_failure;
7154 default:
7155 goto decode_failure;
7156 }
cerion3d870a32005-03-18 12:23:33 +00007157 break;
cerion91ad5362005-01-27 23:02:41 +00007158
7159 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00007160 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00007161
sewardjb51f0f42005-07-18 11:38:02 +00007162 /* Condition Register Logical Instructions */
7163 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
7164 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
7165 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
7166 if (dis_cond_logic( theInstr )) goto decode_success;
7167 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00007168
sewardjb51f0f42005-07-18 11:38:02 +00007169 /* Branch Instructions */
7170 case 0x210: case 0x010: // bcctr, bclr
sewardj9d540e52005-10-08 11:28:16 +00007171 if (dis_branch(theInstr, &dres, resteerOkFn)) goto decode_success;
sewardjb51f0f42005-07-18 11:38:02 +00007172 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00007173
sewardjb51f0f42005-07-18 11:38:02 +00007174 /* Memory Synchronization Instructions */
7175 case 0x096: // isync
7176 if (dis_memsync( theInstr )) goto decode_success;
7177 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00007178
sewardjb51f0f42005-07-18 11:38:02 +00007179 default:
7180 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00007181 }
7182 break;
cerion91ad5362005-01-27 23:02:41 +00007183
7184
cerionb85e8bb2005-02-16 08:54:33 +00007185 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00007186
7187 /* For arith instns, bit10 is the OE flag (overflow enable) */
7188
cerion76de5cf2005-11-18 18:25:12 +00007189 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00007190 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00007191 /* Integer Arithmetic Instructions */
7192 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
7193 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
7194 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
7195 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
7196 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
7197 case 0x0C8: // subfze
7198 if (dis_int_arith( theInstr )) goto decode_success;
7199 goto decode_failure;
7200 default:
7201 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00007202 }
cerion91ad5362005-01-27 23:02:41 +00007203
cerione9d361a2005-03-04 17:35:29 +00007204 /* All remaining opcodes use full 10 bits. */
7205
cerion76de5cf2005-11-18 18:25:12 +00007206 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00007207 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00007208 /* Integer Compare Instructions */
7209 case 0x000: case 0x020: // cmp, cmpl
7210 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007211 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007212
cerione9d361a2005-03-04 17:35:29 +00007213 /* Integer Logical Instructions */
7214 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
7215 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
7216 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
7217 case 0x19C: case 0x13C: // orc, xor
7218 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007219 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00007220
cerione9d361a2005-03-04 17:35:29 +00007221 /* Integer Shift Instructions */
7222 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
7223 case 0x218: // srw
7224 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007225 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007226
cerione9d361a2005-03-04 17:35:29 +00007227 /* Integer Load Instructions */
7228 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
7229 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
7230 case 0x017: case 0x037: // lwzx, lwzux
7231 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007232 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00007233
sewardjb51f0f42005-07-18 11:38:02 +00007234 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00007235 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
7236 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
7237 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007238 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00007239
sewardj602857d2005-09-06 09:10:09 +00007240 /* Integer Load and Store with Byte Reverse Instructions */
7241 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
7242 case 0x296: // stwbrx
7243 if (dis_int_ldst_rev( theInstr )) goto decode_success;
7244 goto decode_failure;
7245
sewardj87e651f2005-09-09 08:31:18 +00007246 /* Integer Load and Store String Instructions */
7247 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
7248 case 0x295: { // stswx
7249 Bool stopHere = False;
7250 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
7251 if (!ok) goto decode_failure;
7252 if (stopHere) {
7253 irbb->next = mkU32(guest_CIA_curr_instr+4);
7254 irbb->jumpkind = Ijk_Boring;
7255 dres.whatNext = Dis_StopHere;
7256 }
7257 goto decode_success;
7258 }
cerion645c9302005-01-31 10:09:59 +00007259
cerione9d361a2005-03-04 17:35:29 +00007260 /* Memory Synchronization Instructions */
7261 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
7262 case 0x256: // sync
7263 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007264 goto decode_failure;
7265
cerione9d361a2005-03-04 17:35:29 +00007266 /* Processor Control Instructions */
7267 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
7268 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
7269 if (dis_proc_ctl( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007270 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00007271
cerione9d361a2005-03-04 17:35:29 +00007272 /* Cache Management Instructions */
7273 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
7274 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
7275 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00007276 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00007277 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00007278 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00007279
sewardjb51f0f42005-07-18 11:38:02 +00007280//zz /* External Control Instructions */
7281//zz case 0x136: case 0x1B6: // eciwx, ecowx
7282//zz DIP("external control op => not implemented\n");
7283//zz goto decode_failure;
7284//zz
7285//zz /* Trap Instructions */
7286//zz case 0x004: // tw
7287//zz DIP("trap op (tw) => not implemented\n");
7288//zz goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00007289
7290 /* Floating Point Load Instructions */
7291 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
7292 case 0x277: // lfdux
sewardj059601a2005-11-13 00:53:05 +00007293 if (!allow_FP) goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00007294 if (dis_fp_load( theInstr )) goto decode_success;
7295 goto decode_failure;
7296
7297 /* Floating Point Store Instructions */
7298 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
7299 case 0x2F7: case 0x3D7: // stfdu, stfiwx
sewardj059601a2005-11-13 00:53:05 +00007300 if (!allow_FP) goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00007301 if (dis_fp_store( theInstr )) goto decode_success;
7302 goto decode_failure;
7303
7304
cerion32aad402005-09-10 12:02:24 +00007305 /* AltiVec instructions */
7306
7307 /* AV Cache Control - Data streams */
7308 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
sewardj059601a2005-11-13 00:53:05 +00007309 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007310 if (dis_av_datastream( theInstr )) goto decode_success;
7311 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00007312
7313 /* AV Load */
7314 case 0x006: case 0x026: // lvsl, lvsr
7315 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
7316 case 0x067: case 0x167: // lvx, lvxl
sewardj059601a2005-11-13 00:53:05 +00007317 if (!allow_VMX) goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00007318 if (dis_av_load( theInstr )) goto decode_success;
7319 goto decode_failure;
7320
7321 /* AV Store */
7322 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
7323 case 0x0E7: case 0x1E7: // stvx, stvxl
sewardj059601a2005-11-13 00:53:05 +00007324 if (!allow_VMX) goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00007325 if (dis_av_store( theInstr )) goto decode_success;
7326 goto decode_failure;
7327
7328 default:
7329 goto decode_failure;
7330 }
7331 break;
7332
7333
cerion32aad402005-09-10 12:02:24 +00007334 case 0x04:
7335 /* AltiVec instructions */
7336
cerion76de5cf2005-11-18 18:25:12 +00007337 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00007338 switch (opc2) {
7339 /* AV Mult-Add, Mult-Sum */
7340 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
7341 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
7342 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
sewardj059601a2005-11-13 00:53:05 +00007343 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007344 if (dis_av_multarith( theInstr )) goto decode_success;
7345 goto decode_failure;
7346
7347 /* AV Permutations */
7348 case 0x2A: // vsel
7349 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00007350 case 0x2C: // vsldoi
sewardj059601a2005-11-13 00:53:05 +00007351 if (!allow_VMX) goto decode_failure;
cerion92d9d872005-09-15 21:58:50 +00007352 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00007353 goto decode_failure;
7354
7355 /* AV Floating Point Mult-Add/Sub */
7356 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
sewardj059601a2005-11-13 00:53:05 +00007357 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007358 if (dis_av_fp_arith( theInstr )) goto decode_success;
7359 goto decode_failure;
7360
7361 default:
7362 break; // Fall through...
7363 }
7364
cerion76de5cf2005-11-18 18:25:12 +00007365 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00007366 switch (opc2) {
7367 /* AV Arithmetic */
7368 case 0x180: // vaddcuw
7369 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
7370 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
7371 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
7372 case 0x580: // vsubcuw
7373 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
7374 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
7375 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
7376 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
7377 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
7378 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
7379 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
7380 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
7381 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
7382 case 0x008: case 0x048: // vmuloub, vmulouh
7383 case 0x108: case 0x148: // vmulosb, vmulosh
7384 case 0x208: case 0x248: // vmuleub, vmuleuh
7385 case 0x308: case 0x348: // vmulesb, vmulesh
7386 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
7387 case 0x688: case 0x788: // vsum2sws, vsumsws
sewardj059601a2005-11-13 00:53:05 +00007388 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007389 if (dis_av_arith( theInstr )) goto decode_success;
7390 goto decode_failure;
7391
7392 /* AV Rotate, Shift */
7393 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
7394 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
7395 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
7396 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
7397 case 0x1C4: case 0x2C4: // vsl, vsr
7398 case 0x40C: case 0x44C: // vslo, vsro
sewardj059601a2005-11-13 00:53:05 +00007399 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007400 if (dis_av_shift( theInstr )) goto decode_success;
7401 goto decode_failure;
7402
7403 /* AV Logic */
7404 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
7405 case 0x4C4: case 0x504: // vxor, vnor
sewardj059601a2005-11-13 00:53:05 +00007406 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007407 if (dis_av_logic( theInstr )) goto decode_success;
7408 goto decode_failure;
7409
7410 /* AV Processor Control */
7411 case 0x604: case 0x644: // mfvscr, mtvscr
sewardj059601a2005-11-13 00:53:05 +00007412 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007413 if (dis_av_procctl( theInstr )) goto decode_success;
7414 goto decode_failure;
7415
7416 /* AV Floating Point Arithmetic */
7417 case 0x00A: case 0x04A: // vaddfp, vsubfp
7418 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
7419 case 0x1CA: // vlogefp
7420 case 0x40A: case 0x44A: // vmaxfp, vminfp
sewardj059601a2005-11-13 00:53:05 +00007421 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007422 if (dis_av_fp_arith( theInstr )) goto decode_success;
7423 goto decode_failure;
7424
7425 /* AV Floating Point Round/Convert */
7426 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
7427 case 0x2CA: // vrfim
7428 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
7429 case 0x3CA: // vctsxs
sewardj059601a2005-11-13 00:53:05 +00007430 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007431 if (dis_av_fp_convert( theInstr )) goto decode_success;
7432 goto decode_failure;
7433
7434 /* AV Merge, Splat */
7435 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
7436 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
7437 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
7438 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
sewardj059601a2005-11-13 00:53:05 +00007439 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007440 if (dis_av_permute( theInstr )) goto decode_success;
7441 goto decode_failure;
7442
7443 /* AV Pack, Unpack */
7444 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
7445 case 0x0CE: // vpkuwus
7446 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
7447 case 0x1CE: // vpkswss
7448 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
7449 case 0x2CE: // vupklsh
7450 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
sewardj059601a2005-11-13 00:53:05 +00007451 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007452 if (dis_av_pack( theInstr )) goto decode_success;
7453 goto decode_failure;
7454
7455 default:
7456 break; // Fall through...
7457 }
7458
cerion76de5cf2005-11-18 18:25:12 +00007459 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00007460 switch (opc2) {
7461
7462 /* AV Compare */
7463 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
7464 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
7465 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
sewardj059601a2005-11-13 00:53:05 +00007466 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007467 if (dis_av_cmp( theInstr )) goto decode_success;
7468 goto decode_failure;
7469
7470 /* AV Floating Point Compare */
7471 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
7472 case 0x3C6: // vcmpbfp
sewardj059601a2005-11-13 00:53:05 +00007473 if (!allow_VMX) goto decode_failure;
cerion32aad402005-09-10 12:02:24 +00007474 if (dis_av_fp_cmp( theInstr )) goto decode_success;
7475 goto decode_failure;
7476
7477 default:
7478 goto decode_failure;
7479 }
7480 break;
cerion7aa4bbc2005-01-29 09:32:07 +00007481
cerion896a1372005-01-25 12:24:25 +00007482 default:
7483 decode_failure:
7484 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00007485 opc2 = (theInstr) & 0x7FF;
cerion1515db92005-01-25 17:21:23 +00007486 vex_printf("disInstr(ppc32): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00007487 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00007488 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00007489 opc1, opc1, opc2, opc2);
cerionb85e8bb2005-02-16 08:54:33 +00007490
cerione9d361a2005-03-04 17:35:29 +00007491#if PPC32_TOIR_DEBUG
cerion995bc362005-02-03 11:03:31 +00007492 vex_printf("disInstr(ppc32): instr: ");
7493 vex_printf_binary( theInstr, 32, True );
7494 vex_printf("\n");
7495
7496 vex_printf("disInstr(ppc32): opcode1: ");
7497 vex_printf_binary( opc1, 6, False );
7498 vex_printf("\n");
7499
7500 vex_printf("disInstr(ppc32): opcode2: ");
7501 vex_printf_binary( opc2, 10, False );
cerion45552a92005-02-03 18:20:22 +00007502 vex_printf("\n\n");
7503#endif
cerion995bc362005-02-03 11:03:31 +00007504
7505
sewardj01a9e802005-02-01 20:46:00 +00007506 /* Tell the dispatcher that this insn cannot be decoded, and so has
7507 not been executed, and (is currently) the next to be executed.
7508 CIA should be up-to-date since it made so at the start of each
7509 insn, but nevertheless be paranoid and update it again right
7510 now. */
sewardjb51f0f42005-07-18 11:38:02 +00007511 putSPR( PPC32_SPR_CIA, mkU32(guest_CIA_curr_instr) );
sewardj9e6491a2005-07-02 19:24:10 +00007512 irbb->next = mkU32(guest_CIA_curr_instr);
sewardj01a9e802005-02-01 20:46:00 +00007513 irbb->jumpkind = Ijk_NoDecode;
sewardj9e6491a2005-07-02 19:24:10 +00007514 dres.whatNext = Dis_StopHere;
7515 dres.len = 0;
7516 return dres;
cerion896a1372005-01-25 12:24:25 +00007517
7518 } /* switch (opc) for the main (primary) opcode switch. */
7519
7520 decode_success:
7521 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00007522 DIP("\n");
7523
sewardj9e6491a2005-07-02 19:24:10 +00007524 dres.len = 4;
7525 return dres;
cerion896a1372005-01-25 12:24:25 +00007526}
7527
7528#undef DIP
7529#undef DIS
7530
sewardj9e6491a2005-07-02 19:24:10 +00007531
7532/*------------------------------------------------------------*/
7533/*--- Top-level fn ---*/
7534/*------------------------------------------------------------*/
7535
7536/* Disassemble a single instruction into IR. The instruction
7537 is located in host memory at &guest_code[delta]. */
7538
7539DisResult disInstr_PPC32 ( IRBB* irbb_IN,
7540 Bool put_IP,
7541 Bool (*resteerOkFn) ( Addr64 ),
7542 UChar* guest_code_IN,
7543 Long delta,
7544 Addr64 guest_IP,
7545 VexArchInfo* archinfo,
7546 Bool host_bigendian_IN )
7547{
7548 DisResult dres;
7549
7550 /* Set globals (see top of this file) */
7551 guest_code = guest_code_IN;
7552 irbb = irbb_IN;
7553 host_is_bigendian = host_bigendian_IN;
7554 guest_CIA_curr_instr = (Addr32)guest_IP;
7555 guest_CIA_bbstart = (Addr32)toUInt(guest_IP - delta);
7556
7557 dres = disInstr_PPC32_WRK ( put_IP, resteerOkFn,
7558 delta, archinfo );
7559
7560 return dres;
7561}
7562
7563
sewardjc808ef72005-08-18 11:50:43 +00007564/*------------------------------------------------------------*/
7565/*--- Unused stuff ---*/
7566/*------------------------------------------------------------*/
7567
7568///* A potentially more memcheck-friendly implementation of Clz32, with
7569// the boundary case Clz32(0) = 32, which is what ppc requires. */
7570//
7571//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
7572//{
7573// /* Welcome ... to SSA R Us. */
7574// IRTemp n1 = newTemp(Ity_I32);
7575// IRTemp n2 = newTemp(Ity_I32);
7576// IRTemp n3 = newTemp(Ity_I32);
7577// IRTemp n4 = newTemp(Ity_I32);
7578// IRTemp n5 = newTemp(Ity_I32);
7579// IRTemp n6 = newTemp(Ity_I32);
7580// IRTemp n7 = newTemp(Ity_I32);
7581// IRTemp n8 = newTemp(Ity_I32);
7582// IRTemp n9 = newTemp(Ity_I32);
7583// IRTemp n10 = newTemp(Ity_I32);
7584// IRTemp n11 = newTemp(Ity_I32);
7585// IRTemp n12 = newTemp(Ity_I32);
7586//
7587// /* First, propagate the most significant 1-bit into all lower
7588// positions in the word. */
7589// /* unsigned int clz ( unsigned int n )
7590// {
7591// n |= (n >> 1);
7592// n |= (n >> 2);
7593// n |= (n >> 4);
7594// n |= (n >> 8);
7595// n |= (n >> 16);
7596// return bitcount(~n);
7597// }
7598// */
7599// assign(n1, mkexpr(arg));
7600// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
7601// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
7602// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
7603// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
7604// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
7605// /* This gives a word of the form 0---01---1. Now invert it, giving
7606// a word of the form 1---10---0, then do a population-count idiom
7607// (to count the 1s, which is the number of leading zeroes, or 32
7608// if the original word was 0. */
7609// assign(n7, unop(Iop_Not32, mkexpr(n6)));
7610//
7611// /* unsigned int bitcount ( unsigned int n )
7612// {
7613// n = n - ((n >> 1) & 0x55555555);
7614// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
7615// n = (n + (n >> 4)) & 0x0F0F0F0F;
7616// n = n + (n >> 8);
7617// n = (n + (n >> 16)) & 0x3F;
7618// return n;
7619// }
7620// */
7621// assign(n8,
7622// binop(Iop_Sub32,
7623// mkexpr(n7),
7624// binop(Iop_And32,
7625// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
7626// mkU32(0x55555555))));
7627// assign(n9,
7628// binop(Iop_Add32,
7629// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
7630// binop(Iop_And32,
7631// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
7632// mkU32(0x33333333))));
7633// assign(n10,
7634// binop(Iop_And32,
7635// binop(Iop_Add32,
7636// mkexpr(n9),
7637// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
7638// mkU32(0x0F0F0F0F)));
7639// assign(n11,
7640// binop(Iop_Add32,
7641// mkexpr(n10),
7642// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
7643// assign(n12,
7644// binop(Iop_Add32,
7645// mkexpr(n11),
7646// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
7647// return
7648// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
7649//}
7650
cerion896a1372005-01-25 12:24:25 +00007651/*--------------------------------------------------------------------*/
cerion1515db92005-01-25 17:21:23 +00007652/*--- end guest-ppc32/toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00007653/*--------------------------------------------------------------------*/