translate.c 80.9 KB
Newer Older
pbrook's avatar
pbrook committed
1
2
/*
 *  m68k translation
3
 *
pbrook's avatar
pbrook committed
4
 *  Copyright (c) 2005-2007 CodeSourcery
pbrook's avatar
pbrook committed
5
6
7
8
9
10
11
12
13
14
15
16
17
 *  Written by Paul Brook
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
pbrook's avatar
pbrook committed
19
20
21
22
 */

#include "cpu.h"
#include "disas.h"
bellard's avatar
bellard committed
23
#include "tcg-op.h"
24
#include "qemu-log.h"
pbrook's avatar
pbrook committed
25

pbrook's avatar
pbrook committed
26
#include "helpers.h"
pbrook's avatar
pbrook committed
27
28
#define GEN_HELPER 1
#include "helpers.h"
pbrook's avatar
pbrook committed
29

pbrook's avatar
pbrook committed
30
31
//#define DEBUG_DISPATCH 1

pbrook's avatar
pbrook committed
32
33
34
35
36
/* Fake floating point.  */
#define tcg_gen_mov_f64 tcg_gen_mov_i64
#define tcg_gen_qemu_ldf64 tcg_gen_qemu_ld64
#define tcg_gen_qemu_stf64 tcg_gen_qemu_st64

pbrook's avatar
pbrook committed
37
#define DEFO32(name, offset) static TCGv QREG_##name;
pbrook's avatar
pbrook committed
38
39
#define DEFO64(name, offset) static TCGv_i64 QREG_##name;
#define DEFF64(name, offset) static TCGv_i64 QREG_##name;
pbrook's avatar
pbrook committed
40
41
42
43
44
#include "qregs.def"
#undef DEFO32
#undef DEFO64
#undef DEFF64

pbrook's avatar
pbrook committed
45
static TCGv_ptr cpu_env;
pbrook's avatar
pbrook committed
46
47
48
49

static char cpu_reg_names[3*8*3 + 5*4];
static TCGv cpu_dregs[8];
static TCGv cpu_aregs[8];
pbrook's avatar
pbrook committed
50
51
static TCGv_i64 cpu_fregs[8];
static TCGv_i64 cpu_macc[4];
pbrook's avatar
pbrook committed
52
53
54
55
56
57
58
59

#define DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7]
#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7]
#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7]
#define MACREG(acc) cpu_macc[acc]
#define QREG_SP cpu_aregs[7]

static TCGv NULL_QREG;
pbrook's avatar
pbrook committed
60
#define IS_NULL_QREG(t) (TCGV_EQUAL(t, NULL_QREG))
pbrook's avatar
pbrook committed
61
62
63
/* Used to distinguish stores from bad addressing modes.  */
static TCGv store_dummy;

pbrook's avatar
pbrook committed
64
65
#include "gen-icount.h"

pbrook's avatar
pbrook committed
66
67
68
69
70
void m68k_tcg_init(void)
{
    char *p;
    int i;

71
72
#define DEFO32(name,  offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUM68KState, offset), #name);
#define DEFO64(name,  offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUM68KState, offset), #name);
pbrook's avatar
pbrook committed
73
74
75
76
77
78
#define DEFF64(name,  offset) DEFO64(name, offset)
#include "qregs.def"
#undef DEFO32
#undef DEFO64
#undef DEFF64

pbrook's avatar
pbrook committed
79
    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
pbrook's avatar
pbrook committed
80
81
82
83

    p = cpu_reg_names;
    for (i = 0; i < 8; i++) {
        sprintf(p, "D%d", i);
pbrook's avatar
pbrook committed
84
        cpu_dregs[i] = tcg_global_mem_new(TCG_AREG0,
pbrook's avatar
pbrook committed
85
86
87
                                          offsetof(CPUM68KState, dregs[i]), p);
        p += 3;
        sprintf(p, "A%d", i);
pbrook's avatar
pbrook committed
88
        cpu_aregs[i] = tcg_global_mem_new(TCG_AREG0,
pbrook's avatar
pbrook committed
89
90
91
                                          offsetof(CPUM68KState, aregs[i]), p);
        p += 3;
        sprintf(p, "F%d", i);
pbrook's avatar
pbrook committed
92
        cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0,
pbrook's avatar
pbrook committed
93
94
95
96
97
                                          offsetof(CPUM68KState, fregs[i]), p);
        p += 3;
    }
    for (i = 0; i < 4; i++) {
        sprintf(p, "ACC%d", i);
pbrook's avatar
pbrook committed
98
        cpu_macc[i] = tcg_global_mem_new_i64(TCG_AREG0,
pbrook's avatar
pbrook committed
99
100
101
102
                                         offsetof(CPUM68KState, macc[i]), p);
        p += 5;
    }

pbrook's avatar
pbrook committed
103
104
    NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL");
    store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL");
pbrook's avatar
pbrook committed
105

pbrook's avatar
pbrook committed
106
#define GEN_HELPER 2
pbrook's avatar
pbrook committed
107
108
109
#include "helpers.h"
}

pbrook's avatar
pbrook committed
110
111
112
113
114
115
116
117
118
119
static inline void qemu_assert(int cond, const char *msg)
{
    if (!cond) {
        fprintf (stderr, "badness: %s\n", msg);
        abort();
    }
}

/* internal defines */
typedef struct DisasContext {
pbrook's avatar
pbrook committed
120
    CPUM68KState *env;
pbrook's avatar
pbrook committed
121
    target_ulong insn_pc; /* Start of the current instruction.  */
pbrook's avatar
pbrook committed
122
123
124
    target_ulong pc;
    int is_jmp;
    int cc_op;
pbrook's avatar
pbrook committed
125
    int user;
pbrook's avatar
pbrook committed
126
127
128
    uint32_t fpcr;
    struct TranslationBlock *tb;
    int singlestep_enabled;
pbrook's avatar
pbrook committed
129
    int is_mem;
pbrook's avatar
pbrook committed
130
131
    TCGv_i64 mactmp;
    int done_mac;
pbrook's avatar
pbrook committed
132
133
134
135
} DisasContext;

#define DISAS_JUMP_NEXT 4

pbrook's avatar
pbrook committed
136
137
138
139
140
141
#if defined(CONFIG_USER_ONLY)
#define IS_USER(s) 1
#else
#define IS_USER(s) s->user
#endif

pbrook's avatar
pbrook committed
142
143
144
145
146
147
148
149
150
151
152
/* XXX: move that elsewhere */
/* ??? Fix exceptions.  */
static void *gen_throws_exception;
#define gen_last_qop NULL

#define OS_BYTE 0
#define OS_WORD 1
#define OS_LONG 2
#define OS_SINGLE 4
#define OS_DOUBLE 5

153
typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn);
pbrook's avatar
pbrook committed
154

pbrook's avatar
pbrook committed
155
#ifdef DEBUG_DISPATCH
156
157
158
159
160
161
162
163
164
165
166
#define DISAS_INSN(name)                                                \
    static void real_disas_##name(CPUM68KState *env, DisasContext *s,   \
                                  uint16_t insn);                       \
    static void disas_##name(CPUM68KState *env, DisasContext *s,        \
                             uint16_t insn)                             \
    {                                                                   \
        qemu_log("Dispatch " #name "\n");                               \
        real_disas_##name(s, env, insn);                                \
    }                                                                   \
    static void real_disas_##name(CPUM68KState *env, DisasContext *s,   \
                                  uint16_t insn)
pbrook's avatar
pbrook committed
167
#else
168
169
170
#define DISAS_INSN(name)                                                \
    static void disas_##name(CPUM68KState *env, DisasContext *s,        \
                             uint16_t insn)
pbrook's avatar
pbrook committed
171
#endif
pbrook's avatar
pbrook committed
172
173
174

/* Generate a load from the specified address.  Narrow values are
   sign extended to full register width.  */
pbrook's avatar
pbrook committed
175
static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
pbrook's avatar
pbrook committed
176
{
pbrook's avatar
pbrook committed
177
178
    TCGv tmp;
    int index = IS_USER(s);
pbrook's avatar
pbrook committed
179
    s->is_mem = 1;
pbrook's avatar
pbrook committed
180
    tmp = tcg_temp_new_i32();
pbrook's avatar
pbrook committed
181
182
183
    switch(opsize) {
    case OS_BYTE:
        if (sign)
pbrook's avatar
pbrook committed
184
            tcg_gen_qemu_ld8s(tmp, addr, index);
pbrook's avatar
pbrook committed
185
        else
pbrook's avatar
pbrook committed
186
            tcg_gen_qemu_ld8u(tmp, addr, index);
pbrook's avatar
pbrook committed
187
188
189
        break;
    case OS_WORD:
        if (sign)
pbrook's avatar
pbrook committed
190
            tcg_gen_qemu_ld16s(tmp, addr, index);
pbrook's avatar
pbrook committed
191
        else
pbrook's avatar
pbrook committed
192
            tcg_gen_qemu_ld16u(tmp, addr, index);
pbrook's avatar
pbrook committed
193
194
195
        break;
    case OS_LONG:
    case OS_SINGLE:
pbrook's avatar
pbrook committed
196
        tcg_gen_qemu_ld32u(tmp, addr, index);
pbrook's avatar
pbrook committed
197
198
199
200
201
202
203
204
        break;
    default:
        qemu_assert(0, "bad load size");
    }
    gen_throws_exception = gen_last_qop;
    return tmp;
}

pbrook's avatar
pbrook committed
205
206
207
208
209
210
211
212
213
214
215
static inline TCGv_i64 gen_load64(DisasContext * s, TCGv addr)
{
    TCGv_i64 tmp;
    int index = IS_USER(s);
    s->is_mem = 1;
    tmp = tcg_temp_new_i64();
    tcg_gen_qemu_ldf64(tmp, addr, index);
    gen_throws_exception = gen_last_qop;
    return tmp;
}

pbrook's avatar
pbrook committed
216
/* Generate a store.  */
pbrook's avatar
pbrook committed
217
static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
pbrook's avatar
pbrook committed
218
{
pbrook's avatar
pbrook committed
219
    int index = IS_USER(s);
pbrook's avatar
pbrook committed
220
    s->is_mem = 1;
pbrook's avatar
pbrook committed
221
222
    switch(opsize) {
    case OS_BYTE:
pbrook's avatar
pbrook committed
223
        tcg_gen_qemu_st8(val, addr, index);
pbrook's avatar
pbrook committed
224
225
        break;
    case OS_WORD:
pbrook's avatar
pbrook committed
226
        tcg_gen_qemu_st16(val, addr, index);
pbrook's avatar
pbrook committed
227
228
229
        break;
    case OS_LONG:
    case OS_SINGLE:
pbrook's avatar
pbrook committed
230
        tcg_gen_qemu_st32(val, addr, index);
pbrook's avatar
pbrook committed
231
232
233
234
235
236
237
        break;
    default:
        qemu_assert(0, "bad store size");
    }
    gen_throws_exception = gen_last_qop;
}

pbrook's avatar
pbrook committed
238
239
240
241
242
243
244
245
static inline void gen_store64(DisasContext *s, TCGv addr, TCGv_i64 val)
{
    int index = IS_USER(s);
    s->is_mem = 1;
    tcg_gen_qemu_stf64(val, addr, index);
    gen_throws_exception = gen_last_qop;
}

pbrook's avatar
pbrook committed
246
247
248
249
250
251
typedef enum {
    EA_STORE,
    EA_LOADU,
    EA_LOADS
} ea_what;

pbrook's avatar
pbrook committed
252
253
/* Generate an unsigned load if VAL is 0 a signed load if val is -1,
   otherwise generate a store.  */
pbrook's avatar
pbrook committed
254
255
static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val,
                     ea_what what)
pbrook's avatar
pbrook committed
256
{
pbrook's avatar
pbrook committed
257
    if (what == EA_STORE) {
pbrook's avatar
pbrook committed
258
        gen_store(s, opsize, addr, val);
pbrook's avatar
pbrook committed
259
        return store_dummy;
pbrook's avatar
pbrook committed
260
    } else {
pbrook's avatar
pbrook committed
261
        return gen_load(s, opsize, addr, what == EA_LOADS);
pbrook's avatar
pbrook committed
262
263
264
    }
}

pbrook's avatar
pbrook committed
265
/* Read a 32-bit immediate constant.  */
266
static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s)
pbrook's avatar
pbrook committed
267
268
{
    uint32_t im;
269
    im = ((uint32_t)cpu_lduw_code(env, s->pc)) << 16;
pbrook's avatar
pbrook committed
270
    s->pc += 2;
271
    im |= cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
272
273
274
275
276
    s->pc += 2;
    return im;
}

/* Calculate and address index.  */
pbrook's avatar
pbrook committed
277
static TCGv gen_addr_index(uint16_t ext, TCGv tmp)
pbrook's avatar
pbrook committed
278
{
pbrook's avatar
pbrook committed
279
    TCGv add;
pbrook's avatar
pbrook committed
280
281
282
283
    int scale;

    add = (ext & 0x8000) ? AREG(ext, 12) : DREG(ext, 12);
    if ((ext & 0x800) == 0) {
pbrook's avatar
pbrook committed
284
        tcg_gen_ext16s_i32(tmp, add);
pbrook's avatar
pbrook committed
285
286
287
288
        add = tmp;
    }
    scale = (ext >> 9) & 3;
    if (scale != 0) {
pbrook's avatar
pbrook committed
289
        tcg_gen_shli_i32(tmp, add, scale);
pbrook's avatar
pbrook committed
290
291
292
293
294
        add = tmp;
    }
    return add;
}

pbrook's avatar
pbrook committed
295
296
/* Handle a base + index + displacement effective addresss.
   A NULL_QREG base means pc-relative.  */
297
298
static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
                            TCGv base)
pbrook's avatar
pbrook committed
299
300
301
{
    uint32_t offset;
    uint16_t ext;
pbrook's avatar
pbrook committed
302
303
    TCGv add;
    TCGv tmp;
pbrook's avatar
pbrook committed
304
    uint32_t bd, od;
pbrook's avatar
pbrook committed
305
306

    offset = s->pc;
307
    ext = cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
308
    s->pc += 2;
pbrook's avatar
pbrook committed
309
310

    if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX))
pbrook's avatar
pbrook committed
311
        return NULL_QREG;
pbrook's avatar
pbrook committed
312
313
314
315

    if (ext & 0x100) {
        /* full extension word format */
        if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL))
pbrook's avatar
pbrook committed
316
            return NULL_QREG;
pbrook's avatar
pbrook committed
317
318
319
320

        if ((ext & 0x30) > 0x10) {
            /* base displacement */
            if ((ext & 0x30) == 0x20) {
321
                bd = (int16_t)cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
322
323
                s->pc += 2;
            } else {
324
                bd = read_im32(env, s);
pbrook's avatar
pbrook committed
325
326
327
328
            }
        } else {
            bd = 0;
        }
pbrook's avatar
pbrook committed
329
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
330
331
332
333
        if ((ext & 0x44) == 0) {
            /* pre-index */
            add = gen_addr_index(ext, tmp);
        } else {
pbrook's avatar
pbrook committed
334
            add = NULL_QREG;
pbrook's avatar
pbrook committed
335
336
337
        }
        if ((ext & 0x80) == 0) {
            /* base not suppressed */
pbrook's avatar
pbrook committed
338
            if (IS_NULL_QREG(base)) {
339
                base = tcg_const_i32(offset + bd);
pbrook's avatar
pbrook committed
340
341
                bd = 0;
            }
pbrook's avatar
pbrook committed
342
343
            if (!IS_NULL_QREG(add)) {
                tcg_gen_add_i32(tmp, add, base);
pbrook's avatar
pbrook committed
344
345
346
347
348
                add = tmp;
            } else {
                add = base;
            }
        }
pbrook's avatar
pbrook committed
349
        if (!IS_NULL_QREG(add)) {
pbrook's avatar
pbrook committed
350
            if (bd != 0) {
pbrook's avatar
pbrook committed
351
                tcg_gen_addi_i32(tmp, add, bd);
pbrook's avatar
pbrook committed
352
353
354
                add = tmp;
            }
        } else {
355
            add = tcg_const_i32(bd);
pbrook's avatar
pbrook committed
356
357
358
359
360
361
        }
        if ((ext & 3) != 0) {
            /* memory indirect */
            base = gen_load(s, OS_LONG, add, 0);
            if ((ext & 0x44) == 4) {
                add = gen_addr_index(ext, tmp);
pbrook's avatar
pbrook committed
362
                tcg_gen_add_i32(tmp, add, base);
pbrook's avatar
pbrook committed
363
364
365
366
367
368
369
                add = tmp;
            } else {
                add = base;
            }
            if ((ext & 3) > 1) {
                /* outer displacement */
                if ((ext & 3) == 2) {
370
                    od = (int16_t)cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
371
372
                    s->pc += 2;
                } else {
373
                    od = read_im32(env, s);
pbrook's avatar
pbrook committed
374
375
376
377
378
                }
            } else {
                od = 0;
            }
            if (od != 0) {
pbrook's avatar
pbrook committed
379
                tcg_gen_addi_i32(tmp, add, od);
pbrook's avatar
pbrook committed
380
381
382
                add = tmp;
            }
        }
pbrook's avatar
pbrook committed
383
    } else {
pbrook's avatar
pbrook committed
384
        /* brief extension word format */
pbrook's avatar
pbrook committed
385
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
386
        add = gen_addr_index(ext, tmp);
pbrook's avatar
pbrook committed
387
388
        if (!IS_NULL_QREG(base)) {
            tcg_gen_add_i32(tmp, add, base);
pbrook's avatar
pbrook committed
389
            if ((int8_t)ext)
pbrook's avatar
pbrook committed
390
                tcg_gen_addi_i32(tmp, tmp, (int8_t)ext);
pbrook's avatar
pbrook committed
391
        } else {
pbrook's avatar
pbrook committed
392
            tcg_gen_addi_i32(tmp, add, offset + (int8_t)ext);
pbrook's avatar
pbrook committed
393
394
        }
        add = tmp;
pbrook's avatar
pbrook committed
395
    }
pbrook's avatar
pbrook committed
396
    return add;
pbrook's avatar
pbrook committed
397
398
399
400
401
402
}

/* Update the CPU env CC_OP state.  */
static inline void gen_flush_cc_op(DisasContext *s)
{
    if (s->cc_op != CC_OP_DYNAMIC)
pbrook's avatar
pbrook committed
403
        tcg_gen_movi_i32(QREG_CC_OP, s->cc_op);
pbrook's avatar
pbrook committed
404
405
406
407
408
409
410
}

/* Evaluate all the CC flags.  */
static inline void gen_flush_flags(DisasContext *s)
{
    if (s->cc_op == CC_OP_FLAGS)
        return;
pbrook's avatar
pbrook committed
411
    gen_flush_cc_op(s);
pbrook's avatar
pbrook committed
412
    gen_helper_flush_flags(cpu_env, QREG_CC_OP);
pbrook's avatar
pbrook committed
413
414
415
    s->cc_op = CC_OP_FLAGS;
}

pbrook's avatar
pbrook committed
416
417
418
419
420
421
422
423
424
425
426
427
static void gen_logic_cc(DisasContext *s, TCGv val)
{
    tcg_gen_mov_i32(QREG_CC_DEST, val);
    s->cc_op = CC_OP_LOGIC;
}

static void gen_update_cc_add(TCGv dest, TCGv src)
{
    tcg_gen_mov_i32(QREG_CC_DEST, dest);
    tcg_gen_mov_i32(QREG_CC_SRC, src);
}

pbrook's avatar
pbrook committed
428
429
430
431
432
433
434
435
436
437
static inline int opsize_bytes(int opsize)
{
    switch (opsize) {
    case OS_BYTE: return 1;
    case OS_WORD: return 2;
    case OS_LONG: return 4;
    case OS_SINGLE: return 4;
    case OS_DOUBLE: return 8;
    default:
        qemu_assert(0, "bad operand size");
438
        return 0;
pbrook's avatar
pbrook committed
439
440
441
442
443
    }
}

/* Assign value to a register.  If the width is less than the register width
   only the low part of the register is set.  */
pbrook's avatar
pbrook committed
444
static void gen_partset_reg(int opsize, TCGv reg, TCGv val)
pbrook's avatar
pbrook committed
445
{
pbrook's avatar
pbrook committed
446
    TCGv tmp;
pbrook's avatar
pbrook committed
447
448
    switch (opsize) {
    case OS_BYTE:
pbrook's avatar
pbrook committed
449
        tcg_gen_andi_i32(reg, reg, 0xffffff00);
pbrook's avatar
pbrook committed
450
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
451
452
        tcg_gen_ext8u_i32(tmp, val);
        tcg_gen_or_i32(reg, reg, tmp);
pbrook's avatar
pbrook committed
453
454
        break;
    case OS_WORD:
pbrook's avatar
pbrook committed
455
        tcg_gen_andi_i32(reg, reg, 0xffff0000);
pbrook's avatar
pbrook committed
456
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
457
458
        tcg_gen_ext16u_i32(tmp, val);
        tcg_gen_or_i32(reg, reg, tmp);
pbrook's avatar
pbrook committed
459
460
461
        break;
    case OS_LONG:
    case OS_SINGLE:
pbrook's avatar
pbrook committed
462
        tcg_gen_mov_i32(reg, val);
pbrook's avatar
pbrook committed
463
464
465
466
467
468
469
470
        break;
    default:
        qemu_assert(0, "Bad operand size");
        break;
    }
}

/* Sign or zero extend a value.  */
pbrook's avatar
pbrook committed
471
static inline TCGv gen_extend(TCGv val, int opsize, int sign)
pbrook's avatar
pbrook committed
472
{
pbrook's avatar
pbrook committed
473
    TCGv tmp;
pbrook's avatar
pbrook committed
474
475
476

    switch (opsize) {
    case OS_BYTE:
pbrook's avatar
pbrook committed
477
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
478
        if (sign)
pbrook's avatar
pbrook committed
479
            tcg_gen_ext8s_i32(tmp, val);
pbrook's avatar
pbrook committed
480
        else
pbrook's avatar
pbrook committed
481
            tcg_gen_ext8u_i32(tmp, val);
pbrook's avatar
pbrook committed
482
483
        break;
    case OS_WORD:
pbrook's avatar
pbrook committed
484
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
485
        if (sign)
pbrook's avatar
pbrook committed
486
            tcg_gen_ext16s_i32(tmp, val);
pbrook's avatar
pbrook committed
487
        else
pbrook's avatar
pbrook committed
488
            tcg_gen_ext16u_i32(tmp, val);
pbrook's avatar
pbrook committed
489
490
491
        break;
    case OS_LONG:
    case OS_SINGLE:
pbrook's avatar
pbrook committed
492
        tmp = val;
pbrook's avatar
pbrook committed
493
494
495
496
497
498
499
500
        break;
    default:
        qemu_assert(0, "Bad operand size");
    }
    return tmp;
}

/* Generate code for an "effective address".  Does not adjust the base
aurel32's avatar
aurel32 committed
501
   register for autoincrement addressing modes.  */
502
503
static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn,
                    int opsize)
pbrook's avatar
pbrook committed
504
{
pbrook's avatar
pbrook committed
505
506
    TCGv reg;
    TCGv tmp;
pbrook's avatar
pbrook committed
507
508
509
510
511
512
    uint16_t ext;
    uint32_t offset;

    switch ((insn >> 3) & 7) {
    case 0: /* Data register direct.  */
    case 1: /* Address register direct.  */
pbrook's avatar
pbrook committed
513
        return NULL_QREG;
pbrook's avatar
pbrook committed
514
515
    case 2: /* Indirect register */
    case 3: /* Indirect postincrement.  */
pbrook's avatar
pbrook committed
516
        return AREG(insn, 0);
pbrook's avatar
pbrook committed
517
    case 4: /* Indirect predecrememnt.  */
pbrook's avatar
pbrook committed
518
        reg = AREG(insn, 0);
pbrook's avatar
pbrook committed
519
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
520
        tcg_gen_subi_i32(tmp, reg, opsize_bytes(opsize));
pbrook's avatar
pbrook committed
521
522
        return tmp;
    case 5: /* Indirect displacement.  */
pbrook's avatar
pbrook committed
523
        reg = AREG(insn, 0);
pbrook's avatar
pbrook committed
524
        tmp = tcg_temp_new();
525
        ext = cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
526
        s->pc += 2;
pbrook's avatar
pbrook committed
527
        tcg_gen_addi_i32(tmp, reg, (int16_t)ext);
pbrook's avatar
pbrook committed
528
529
        return tmp;
    case 6: /* Indirect index + displacement.  */
pbrook's avatar
pbrook committed
530
        reg = AREG(insn, 0);
531
        return gen_lea_indexed(env, s, opsize, reg);
pbrook's avatar
pbrook committed
532
    case 7: /* Other */
pbrook's avatar
pbrook committed
533
        switch (insn & 7) {
pbrook's avatar
pbrook committed
534
        case 0: /* Absolute short.  */
535
            offset = cpu_ldsw_code(env, s->pc);
pbrook's avatar
pbrook committed
536
            s->pc += 2;
537
            return tcg_const_i32(offset);
pbrook's avatar
pbrook committed
538
        case 1: /* Absolute long.  */
539
            offset = read_im32(env, s);
540
            return tcg_const_i32(offset);
pbrook's avatar
pbrook committed
541
542
        case 2: /* pc displacement  */
            offset = s->pc;
543
            offset += cpu_ldsw_code(env, s->pc);
pbrook's avatar
pbrook committed
544
            s->pc += 2;
545
            return tcg_const_i32(offset);
pbrook's avatar
pbrook committed
546
        case 3: /* pc index+displacement.  */
547
            return gen_lea_indexed(env, s, opsize, NULL_QREG);
pbrook's avatar
pbrook committed
548
549
        case 4: /* Immediate.  */
        default:
pbrook's avatar
pbrook committed
550
            return NULL_QREG;
pbrook's avatar
pbrook committed
551
552
553
        }
    }
    /* Should never happen.  */
pbrook's avatar
pbrook committed
554
    return NULL_QREG;
pbrook's avatar
pbrook committed
555
556
557
558
}

/* Helper function for gen_ea. Reuse the computed address between the
   for read/write operands.  */
559
560
561
static inline TCGv gen_ea_once(CPUM68KState *env, DisasContext *s,
                               uint16_t insn, int opsize, TCGv val,
                               TCGv *addrp, ea_what what)
pbrook's avatar
pbrook committed
562
{
pbrook's avatar
pbrook committed
563
    TCGv tmp;
pbrook's avatar
pbrook committed
564

pbrook's avatar
pbrook committed
565
    if (addrp && what == EA_STORE) {
pbrook's avatar
pbrook committed
566
567
        tmp = *addrp;
    } else {
568
        tmp = gen_lea(env, s, insn, opsize);
pbrook's avatar
pbrook committed
569
570
        if (IS_NULL_QREG(tmp))
            return tmp;
pbrook's avatar
pbrook committed
571
572
573
        if (addrp)
            *addrp = tmp;
    }
pbrook's avatar
pbrook committed
574
    return gen_ldst(s, opsize, tmp, val, what);
pbrook's avatar
pbrook committed
575
576
577
578
579
}

/* Generate code to load/store a value ito/from an EA.  If VAL > 0 this is
   a write otherwise it is a read (0 == sign extend, -1 == zero extend).
   ADDRP is non-null for readwrite operands.  */
580
581
static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn,
                   int opsize, TCGv val, TCGv *addrp, ea_what what)
pbrook's avatar
pbrook committed
582
{
pbrook's avatar
pbrook committed
583
584
    TCGv reg;
    TCGv result;
pbrook's avatar
pbrook committed
585
586
587
588
    uint32_t offset;

    switch ((insn >> 3) & 7) {
    case 0: /* Data register direct.  */
pbrook's avatar
pbrook committed
589
590
        reg = DREG(insn, 0);
        if (what == EA_STORE) {
pbrook's avatar
pbrook committed
591
            gen_partset_reg(opsize, reg, val);
pbrook's avatar
pbrook committed
592
            return store_dummy;
pbrook's avatar
pbrook committed
593
        } else {
pbrook's avatar
pbrook committed
594
            return gen_extend(reg, opsize, what == EA_LOADS);
pbrook's avatar
pbrook committed
595
596
        }
    case 1: /* Address register direct.  */
pbrook's avatar
pbrook committed
597
598
599
600
        reg = AREG(insn, 0);
        if (what == EA_STORE) {
            tcg_gen_mov_i32(reg, val);
            return store_dummy;
pbrook's avatar
pbrook committed
601
        } else {
pbrook's avatar
pbrook committed
602
            return gen_extend(reg, opsize, what == EA_LOADS);
pbrook's avatar
pbrook committed
603
604
        }
    case 2: /* Indirect register */
pbrook's avatar
pbrook committed
605
606
        reg = AREG(insn, 0);
        return gen_ldst(s, opsize, reg, val, what);
pbrook's avatar
pbrook committed
607
    case 3: /* Indirect postincrement.  */
pbrook's avatar
pbrook committed
608
609
        reg = AREG(insn, 0);
        result = gen_ldst(s, opsize, reg, val, what);
pbrook's avatar
pbrook committed
610
611
        /* ??? This is not exception safe.  The instruction may still
           fault after this point.  */
pbrook's avatar
pbrook committed
612
613
        if (what == EA_STORE || !addrp)
            tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
pbrook's avatar
pbrook committed
614
615
616
        return result;
    case 4: /* Indirect predecrememnt.  */
        {
pbrook's avatar
pbrook committed
617
618
            TCGv tmp;
            if (addrp && what == EA_STORE) {
pbrook's avatar
pbrook committed
619
620
                tmp = *addrp;
            } else {
621
                tmp = gen_lea(env, s, insn, opsize);
pbrook's avatar
pbrook committed
622
623
                if (IS_NULL_QREG(tmp))
                    return tmp;
pbrook's avatar
pbrook committed
624
625
626
                if (addrp)
                    *addrp = tmp;
            }
pbrook's avatar
pbrook committed
627
            result = gen_ldst(s, opsize, tmp, val, what);
pbrook's avatar
pbrook committed
628
629
            /* ??? This is not exception safe.  The instruction may still
               fault after this point.  */
pbrook's avatar
pbrook committed
630
631
632
            if (what == EA_STORE || !addrp) {
                reg = AREG(insn, 0);
                tcg_gen_mov_i32(reg, tmp);
pbrook's avatar
pbrook committed
633
634
635
636
637
            }
        }
        return result;
    case 5: /* Indirect displacement.  */
    case 6: /* Indirect index + displacement.  */
638
        return gen_ea_once(env, s, insn, opsize, val, addrp, what);
pbrook's avatar
pbrook committed
639
    case 7: /* Other */
pbrook's avatar
pbrook committed
640
        switch (insn & 7) {
pbrook's avatar
pbrook committed
641
642
643
644
        case 0: /* Absolute short.  */
        case 1: /* Absolute long.  */
        case 2: /* pc displacement  */
        case 3: /* pc index+displacement.  */
645
            return gen_ea_once(env, s, insn, opsize, val, addrp, what);
pbrook's avatar
pbrook committed
646
647
648
649
        case 4: /* Immediate.  */
            /* Sign extend values for consistency.  */
            switch (opsize) {
            case OS_BYTE:
650
                if (what == EA_LOADS) {
651
                    offset = cpu_ldsb_code(env, s->pc + 1);
652
                } else {
653
                    offset = cpu_ldub_code(env, s->pc + 1);
654
                }
pbrook's avatar
pbrook committed
655
656
657
                s->pc += 2;
                break;
            case OS_WORD:
658
                if (what == EA_LOADS) {
659
                    offset = cpu_ldsw_code(env, s->pc);
660
                } else {
661
                    offset = cpu_lduw_code(env, s->pc);
662
                }
pbrook's avatar
pbrook committed
663
664
665
                s->pc += 2;
                break;
            case OS_LONG:
666
                offset = read_im32(env, s);
pbrook's avatar
pbrook committed
667
668
669
670
                break;
            default:
                qemu_assert(0, "Bad immediate operand");
            }
pbrook's avatar
pbrook committed
671
            return tcg_const_i32(offset);
pbrook's avatar
pbrook committed
672
        default:
pbrook's avatar
pbrook committed
673
            return NULL_QREG;
pbrook's avatar
pbrook committed
674
675
676
        }
    }
    /* Should never happen.  */
pbrook's avatar
pbrook committed
677
    return NULL_QREG;
pbrook's avatar
pbrook committed
678
679
}

pbrook's avatar
pbrook committed
680
/* This generates a conditional branch, clobbering all temporaries.  */
pbrook's avatar
pbrook committed
681
682
static void gen_jmpcc(DisasContext *s, int cond, int l1)
{
pbrook's avatar
pbrook committed
683
    TCGv tmp;
pbrook's avatar
pbrook committed
684

pbrook's avatar
pbrook committed
685
686
    /* TODO: Optimize compare/branch pairs rather than always flushing
       flag state to CC_OP_FLAGS.  */
pbrook's avatar
pbrook committed
687
688
689
    gen_flush_flags(s);
    switch (cond) {
    case 0: /* T */
pbrook's avatar
pbrook committed
690
        tcg_gen_br(l1);
pbrook's avatar
pbrook committed
691
692
693
694
        break;
    case 1: /* F */
        break;
    case 2: /* HI (!C && !Z) */
pbrook's avatar
pbrook committed
695
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
696
697
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
698
699
        break;
    case 3: /* LS (C || Z) */
pbrook's avatar
pbrook committed
700
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
701
702
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
703
704
        break;
    case 4: /* CC (!C) */
pbrook's avatar
pbrook committed
705
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
706
707
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
708
709
        break;
    case 5: /* CS (C) */
pbrook's avatar
pbrook committed
710
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
711
712
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
713
714
        break;
    case 6: /* NE (!Z) */
pbrook's avatar
pbrook committed
715
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
716
717
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
718
719
        break;
    case 7: /* EQ (Z) */
pbrook's avatar
pbrook committed
720
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
721
722
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
723
724
        break;
    case 8: /* VC (!V) */
pbrook's avatar
pbrook committed
725
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
726
727
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
728
729
        break;
    case 9: /* VS (V) */
pbrook's avatar
pbrook committed
730
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
731
732
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
733
734
        break;
    case 10: /* PL (!N) */
pbrook's avatar
pbrook committed
735
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
736
737
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
738
739
        break;
    case 11: /* MI (N) */
pbrook's avatar
pbrook committed
740
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
741
742
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
743
744
        break;
    case 12: /* GE (!(N ^ V)) */
pbrook's avatar
pbrook committed
745
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
746
747
748
749
750
        assert(CCF_V == (CCF_N >> 2));
        tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2);
        tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
        tcg_gen_andi_i32(tmp, tmp, CCF_V);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
751
752
        break;
    case 13: /* LT (N ^ V) */
pbrook's avatar
pbrook committed
753
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
754
755
756
757
758
        assert(CCF_V == (CCF_N >> 2));
        tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2);
        tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
        tcg_gen_andi_i32(tmp, tmp, CCF_V);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
759
760
        break;
    case 14: /* GT (!(Z || (N ^ V))) */
pbrook's avatar
pbrook committed
761
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
762
763
764
765
766
767
        assert(CCF_V == (CCF_N >> 2));
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
        tcg_gen_shri_i32(tmp, tmp, 2);
        tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
        tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
pbrook's avatar
pbrook committed
768
769
        break;
    case 15: /* LE (Z || (N ^ V)) */
pbrook's avatar
pbrook committed
770
        tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
771
772
773
774
775
776
        assert(CCF_V == (CCF_N >> 2));
        tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
        tcg_gen_shri_i32(tmp, tmp, 2);
        tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
        tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z);
        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
pbrook's avatar
pbrook committed
777
778
779
780
781
782
783
784
785
786
787
        break;
    default:
        /* Should ever happen.  */
        abort();
    }
}

DISAS_INSN(scc)
{
    int l1;
    int cond;
pbrook's avatar
pbrook committed
788
    TCGv reg;
pbrook's avatar
pbrook committed
789
790
791
792

    l1 = gen_new_label();
    cond = (insn >> 8) & 0xf;
    reg = DREG(insn, 0);
pbrook's avatar
pbrook committed
793
794
795
    tcg_gen_andi_i32(reg, reg, 0xffffff00);
    /* This is safe because we modify the reg directly, with no other values
       live.  */
pbrook's avatar
pbrook committed
796
    gen_jmpcc(s, cond ^ 1, l1);
pbrook's avatar
pbrook committed
797
    tcg_gen_ori_i32(reg, reg, 0xff);
pbrook's avatar
pbrook committed
798
799
800
    gen_set_label(l1);
}

pbrook's avatar
pbrook committed
801
802
803
804
/* Force a TB lookup after an instruction that changes the CPU state.  */
static void gen_lookup_tb(DisasContext *s)
{
    gen_flush_cc_op(s);
pbrook's avatar
pbrook committed
805
    tcg_gen_movi_i32(QREG_PC, s->pc);
pbrook's avatar
pbrook committed
806
807
808
    s->is_jmp = DISAS_UPDATE;
}

pbrook's avatar
pbrook committed
809
810
811
812
813
814
815
816
817
818
/* Generate a jump to an immediate address.  */
static void gen_jmp_im(DisasContext *s, uint32_t dest)
{
    gen_flush_cc_op(s);
    tcg_gen_movi_i32(QREG_PC, dest);
    s->is_jmp = DISAS_JUMP;
}

/* Generate a jump to the address in qreg DEST.  */
static void gen_jmp(DisasContext *s, TCGv dest)
pbrook's avatar
pbrook committed
819
820
{
    gen_flush_cc_op(s);
pbrook's avatar
pbrook committed
821
    tcg_gen_mov_i32(QREG_PC, dest);
pbrook's avatar
pbrook committed
822
823
824
825
826
827
    s->is_jmp = DISAS_JUMP;
}

static void gen_exception(DisasContext *s, uint32_t where, int nr)
{
    gen_flush_cc_op(s);
pbrook's avatar
pbrook committed
828
    gen_jmp_im(s, where);
829
    gen_helper_raise_exception(cpu_env, tcg_const_i32(nr));
pbrook's avatar
pbrook committed
830
831
}

pbrook's avatar
pbrook committed
832
833
834
835
836
static inline void gen_addr_fault(DisasContext *s)
{
    gen_exception(s, s->insn_pc, EXCP_ADDRESS);
}

837
838
839
840
841
842
843
#define SRC_EA(env, result, opsize, op_sign, addrp) do {                \
        result = gen_ea(env, s, insn, opsize, NULL_QREG, addrp,         \
                        op_sign ? EA_LOADS : EA_LOADU);                 \
        if (IS_NULL_QREG(result)) {                                     \
            gen_addr_fault(s);                                          \
            return;                                                     \
        }                                                               \
pbrook's avatar
pbrook committed
844
845
    } while (0)

846
847
848
849
850
851
#define DEST_EA(env, insn, opsize, val, addrp) do {                     \
        TCGv ea_result = gen_ea(env, s, insn, opsize, val, addrp, EA_STORE); \
        if (IS_NULL_QREG(ea_result)) {                                  \
            gen_addr_fault(s);                                          \
            return;                                                     \
        }                                                               \
pbrook's avatar
pbrook committed
852
853
    } while (0)

pbrook's avatar
pbrook committed
854
855
856
857
858
859
/* Generate a jump to an immediate address.  */
static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
{
    TranslationBlock *tb;

    tb = s->tb;
860
    if (unlikely(s->singlestep_enabled)) {
pbrook's avatar
pbrook committed
861
862
863
        gen_exception(s, dest, EXCP_DEBUG);
    } else if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) ||
               (s->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
bellard's avatar
bellard committed
864
        tcg_gen_goto_tb(n);
pbrook's avatar
pbrook committed
865
        tcg_gen_movi_i32(QREG_PC, dest);
866
        tcg_gen_exit_tb((tcg_target_long)tb + n);
pbrook's avatar
pbrook committed
867
    } else {
pbrook's avatar
pbrook committed
868
        gen_jmp_im(s, dest);
bellard's avatar
bellard committed
869
        tcg_gen_exit_tb(0);
pbrook's avatar
pbrook committed
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
    }
    s->is_jmp = DISAS_TB_JUMP;
}

DISAS_INSN(undef_mac)
{
    gen_exception(s, s->pc - 2, EXCP_LINEA);
}

DISAS_INSN(undef_fpu)
{
    gen_exception(s, s->pc - 2, EXCP_LINEF);
}

DISAS_INSN(undef)
{
    gen_exception(s, s->pc - 2, EXCP_UNSUPPORTED);
887
    cpu_abort(env, "Illegal instruction: %04x @ %08x", insn, s->pc - 2);
pbrook's avatar
pbrook committed
888
889
890
891
}

DISAS_INSN(mulw)
{
pbrook's avatar
pbrook committed
892
893
894
    TCGv reg;
    TCGv tmp;
    TCGv src;
pbrook's avatar
pbrook committed
895
896
897
898
    int sign;

    sign = (insn & 0x100) != 0;
    reg = DREG(insn, 9);
pbrook's avatar
pbrook committed
899
    tmp = tcg_temp_new();
pbrook's avatar
pbrook committed
900
    if (sign)
pbrook's avatar
pbrook committed
901
        tcg_gen_ext16s_i32(tmp, reg);
pbrook's avatar
pbrook committed
902
    else
pbrook's avatar
pbrook committed
903
        tcg_gen_ext16u_i32(tmp, reg);
904
    SRC_EA(env, src, OS_WORD, sign, NULL);
pbrook's avatar
pbrook committed
905
906
    tcg_gen_mul_i32(tmp, tmp, src);
    tcg_gen_mov_i32(reg, tmp);
pbrook's avatar
pbrook committed
907
908
909
910
911
912
    /* Unlike m68k, coldfire always clears the overflow bit.  */
    gen_logic_cc(s, tmp);
}

DISAS_INSN(divw)
{
pbrook's avatar
pbrook committed
913
914
915
    TCGv reg;
    TCGv tmp;
    TCGv src;
pbrook's avatar
pbrook committed
916
917
918
919
920
    int sign;

    sign = (insn & 0x100) != 0;
    reg = DREG(insn, 9);
    if (sign) {
pbrook's avatar
pbrook committed
921
        tcg_gen_ext16s_i32(QREG_DIV1, reg);
pbrook's avatar
pbrook committed
922
    } else {
pbrook's avatar
pbrook committed
923
        tcg_gen_ext16u_i32(QREG_DIV1, reg);
pbrook's avatar
pbrook committed
924
    }
925
    SRC_EA(env, src, OS_WORD, sign, NULL);
pbrook's avatar
pbrook committed
926
    tcg_gen_mov_i32(QREG_DIV2, src);
pbrook's avatar
pbrook committed
927
    if (sign) {
pbrook's avatar
pbrook committed
928
        gen_helper_divs(cpu_env, tcg_const_i32(1));
pbrook's avatar
pbrook committed
929
    } else {
pbrook's avatar
pbrook committed
930
        gen_helper_divu(cpu_env, tcg_const_i32(1));
pbrook's avatar
pbrook committed
931
932
    }

pbrook's avatar
pbrook committed
933
934
    tmp = tcg_temp_new();
    src = tcg_temp_new();
pbrook's avatar
pbrook committed
935
936
937
    tcg_gen_ext16u_i32(tmp, QREG_DIV1);
    tcg_gen_shli_i32(src, QREG_DIV2, 16);
    tcg_gen_or_i32(reg, tmp, src);
pbrook's avatar
pbrook committed
938
939
940
941
942
    s->cc_op = CC_OP_FLAGS;
}

DISAS_INSN(divl)
{
pbrook's avatar
pbrook committed
943
944
945
    TCGv num;
    TCGv den;
    TCGv reg;
pbrook's avatar
pbrook committed
946
947
    uint16_t ext;

948
    ext = cpu_lduw_code(env, s->pc);
pbrook's avatar
pbrook committed
949
950
951
952
953
954
955
    s->pc += 2;
    if (ext & 0x87f8) {
        gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
        return;
    }
    num = DREG(ext, 12);
    reg = DREG(ext, 0);
pbrook's avatar
pbrook committed
956
    tcg_gen_mov_i32(QREG_DIV1, num);
957
    SRC_EA(env, den, OS_LONG, 0, NULL);
pbrook's avatar
pbrook committed
958
    tcg_gen_mov_i32(QREG_DIV2, den);
pbrook's avatar
pbrook committed
959
    if (ext & 0x0800) {
pbrook's avatar
pbrook committed
960
        gen_helper_divs(cpu_env, tcg_const_i32(0));
pbrook's avatar
pbrook committed
961
    } else {
pbrook's avatar
pbrook committed
962
        gen_helper_divu(cpu_env, tcg_const_i32(0));
pbrook's avatar
pbrook committed
963
    }
pbrook's avatar
pbrook committed
964
    if ((ext & 7) == ((ext >> 12) & 7)) {
pbrook's avatar
pbrook committed
965
        /* div */
pbrook's avatar
pbrook committed
966
        tcg_gen_mov_i32 (reg, QREG_DIV1);
pbrook's avatar
pbrook committed
967
968
    } else {
        /* rem */
pbrook's avatar
pbrook committed
969
        tcg_gen_mov_i32 (reg, QREG_DIV2);
pbrook's avatar
pbrook committed
970
971
972
973
974
975
    }
    s->cc_op = CC_OP_FLAGS;
}

DISAS_INSN(addsub)
{
pbrook's avatar
pbrook committed
976
977
978
979
980
    TCGv reg;
    TCGv dest;
    TCGv src;
    TCGv tmp;
    TCGv addr;
pbrook's avatar
pbrook committed
981
982
983
984
    int add;

    add = (insn & 0x4000) != 0;
    reg = DREG(insn, 9);
pbrook's avatar
pbrook committed
985
    dest = tcg_temp_new();
pbrook's avatar
pbrook committed
986
    if (insn & 0x100) {
987
        SRC_EA(env, tmp, OS_LONG, 0, &addr);
pbrook's avatar
pbrook committed
988
989
990
        src = reg;
    } else {
        tmp = reg;
991
        SRC_EA(env, src, OS_LONG, 0, NULL);
pbrook's avatar
pbrook committed
992
993
    }
    if (add) {
pbrook's avatar
pbrook committed
994
995
        tcg_gen_add_i32(dest, tmp, src);
        gen_helper_xflag_lt(QREG_CC_X, dest, src);
pbrook's avatar
pbrook committed
996
997
        s->cc_op = CC_OP_ADD;
    } else {
pbrook's avatar
pbrook committed
998
999
        gen_helper_xflag_lt(QREG_CC_X, tmp, src);
        tcg_gen_sub_i32(dest, tmp, src);
pbrook's avatar
pbrook committed
1000
        s->cc_op = CC_OP_SUB;