Statistics
| Branch: | Revision:

root / target-mips / op_helper.c @ 6af0bf9c

History | View | Annotate | Download (14.9 kB)

1
/*
2
 *  MIPS emulation helpers for qemu.
3
 * 
4
 *  Copyright (c) 2004-2005 Jocelyn Mayer
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
#include <math.h>
21
#include "exec.h"
22

    
23
#define MIPS_DEBUG_DISAS
24

    
25
/*****************************************************************************/
26
/* Exceptions processing helpers */
27
void cpu_loop_exit(void)
28
{
29
    longjmp(env->jmp_env, 1);
30
}
31

    
32
__attribute__ (( regparm(2) ))
33
void do_raise_exception_err (uint32_t exception, int error_code)
34
{
35
#if 1
36
    if (logfile && exception < 0x100)
37
        fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
38
#endif
39
    env->exception_index = exception;
40
    env->error_code = error_code;
41
    T0 = 0;
42
    cpu_loop_exit();
43
}
44

    
45
__attribute__ (( regparm(1) ))
46
void do_raise_exception (uint32_t exception)
47
{
48
    do_raise_exception_err(exception, 0);
49
}
50

    
51
#define MEMSUFFIX _raw
52
#include "op_helper_mem.c"
53
#undef MEMSUFFIX
54
#if !defined(CONFIG_USER_ONLY)
55
#define MEMSUFFIX _user
56
#include "op_helper_mem.c"
57
#undef MEMSUFFIX
58
#define MEMSUFFIX _kernel
59
#include "op_helper_mem.c"
60
#undef MEMSUFFIX
61
#endif
62

    
63
/* 64 bits arithmetic for 32 bits hosts */
64
#if (HOST_LONG_BITS == 32)
65
static inline uint64_t get_HILO (void)
66
{
67
    return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
68
}
69

    
70
static inline void set_HILO (uint64_t HILO)
71
{
72
    env->LO = HILO & 0xFFFFFFFF;
73
    env->HI = HILO >> 32;
74
}
75

    
76
void do_mult (void)
77
{
78
    set_HILO((int64_t)T0 * (int64_t)T1);
79
}
80

    
81
void do_multu (void)
82
{
83
    set_HILO((uint64_t)T0 * (uint64_t)T1);
84
}
85

    
86
void do_madd (void)
87
{
88
    int64_t tmp;
89

    
90
    tmp = ((int64_t)T0 * (int64_t)T1);
91
    set_HILO((int64_t)get_HILO() + tmp);
92
}
93

    
94
void do_maddu (void)
95
{
96
    uint64_t tmp;
97

    
98
    tmp = ((uint64_t)T0 * (uint64_t)T1);
99
    set_HILO(get_HILO() + tmp);
100
}
101

    
102
void do_msub (void)
103
{
104
    int64_t tmp;
105

    
106
    tmp = ((int64_t)T0 * (int64_t)T1);
107
    set_HILO((int64_t)get_HILO() - tmp);
108
}
109

    
110
void do_msubu (void)
111
{
112
    uint64_t tmp;
113

    
114
    tmp = ((uint64_t)T0 * (uint64_t)T1);
115
    set_HILO(get_HILO() - tmp);
116
}
117
#endif
118

    
119
/* CP0 helpers */
120
__attribute__ (( regparm(2) ))
121
void do_mfc0 (int reg, int sel)
122
{
123
    const unsigned char *rn;
124

    
125
    if (sel != 0 && reg != 16 && reg != 28) {
126
        rn = "invalid";
127
        goto print;
128
    }
129
    switch (reg) {
130
    case 0:
131
        T0 = env->CP0_index;
132
        rn = "Index";
133
        break;
134
    case 1:
135
        T0 = cpu_mips_get_random(env);
136
        rn = "Random";
137
        break;
138
    case 2:
139
        T0 = env->CP0_EntryLo0;
140
        rn = "EntryLo0";
141
        break;
142
    case 3:
143
        T0 = env->CP0_EntryLo1;
144
        rn = "EntryLo1";
145
        break;
146
    case 4:
147
        T0 = env->CP0_Context;
148
        rn = "Context";
149
        break;
150
    case 5:
151
        T0 = env->CP0_PageMask;
152
        rn = "PageMask";
153
        break;
154
    case 6:
155
        T0 = env->CP0_Wired;
156
        rn = "Wired";
157
        break;
158
    case 8:
159
        T0 = env->CP0_BadVAddr;
160
        rn = "BadVaddr";
161
        break;
162
    case 9:
163
        T0 = cpu_mips_get_count(env);
164
        rn = "Count";
165
        break;
166
    case 10:
167
        T0 = env->CP0_EntryHi;
168
        rn = "EntryHi";
169
        break;
170
    case 11:
171
        T0 = env->CP0_Compare;
172
        rn = "Compare";
173
        break;
174
    case 12:
175
        T0 = env->CP0_Status;
176
        if (env->hflags & MIPS_HFLAG_UM)
177
            T0 |= CP0St_UM;
178
        if (env->hflags & MIPS_HFLAG_ERL)
179
            T0 |= CP0St_ERL;
180
        if (env->hflags & MIPS_HFLAG_EXL)
181
            T0 |= CP0St_EXL;
182
        rn = "Status";
183
        break;
184
    case 13:
185
        T0 = env->CP0_Cause;
186
        rn = "Cause";
187
        break;
188
    case 14:
189
        T0 = env->CP0_EPC;
190
        rn = "EPC";
191
        break;
192
    case 15:
193
        T0 = env->CP0_PRid;
194
        rn = "PRid";
195
        break;
196
    case 16:
197
        switch (sel) {
198
        case 0:
199
            T0 = env->CP0_Config0;
200
            rn = "Config";
201
            break;
202
        case 1:
203
            T0 = env->CP0_Config1;
204
            rn = "Config1";
205
            break;
206
        default:
207
            rn = "Unknown config register";
208
            break;
209
        }
210
        break;
211
    case 17:
212
        T0 = env->CP0_LLAddr >> 4;
213
        rn = "LLAddr";
214
        break;
215
    case 18:
216
        T0 = env->CP0_WatchLo;
217
        rn = "WatchLo";
218
        break;
219
    case 19:
220
        T0 = env->CP0_WatchHi;
221
        rn = "WatchHi";
222
        break;
223
    case 23:
224
        T0 = env->CP0_Debug;
225
        if (env->hflags & MIPS_HFLAG_DM)
226
            T0 |= 1 << CP0DB_DM;
227
        rn = "Debug";
228
        break;
229
    case 24:
230
        T0 = env->CP0_DEPC;
231
        rn = "DEPC";
232
        break;
233
    case 28:
234
        switch (sel) {
235
        case 0:
236
            T0 = env->CP0_TagLo;
237
            rn = "TagLo";
238
            break;
239
        case 1:
240
            T0 = env->CP0_DataLo;
241
            rn = "DataLo";
242
            break;
243
        default:
244
            rn = "unknown sel";
245
            break;
246
        }
247
        break;
248
    case 30:
249
        T0 = env->CP0_ErrorEPC;
250
        rn = "ErrorEPC";
251
        break;
252
    case 31:
253
        T0 = env->CP0_DESAVE;
254
        rn = "DESAVE";
255
        break;
256
    default:
257
        rn = "unknown";
258
        break;
259
    }
260
 print:
261
#if defined MIPS_DEBUG_DISAS
262
    if (loglevel & CPU_LOG_TB_IN_ASM) {
263
        fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
264
                env->PC, rn, T0, reg, sel);
265
    }
266
#endif
267
    return;
268
}
269

    
270
__attribute__ (( regparm(2) ))
271
void do_mtc0 (int reg, int sel)
272
{
273
    const unsigned char *rn;
274
    uint32_t val, old, mask;
275
    int i, raise;
276

    
277
    if (sel != 0 && reg != 16 && reg != 28) {
278
        val = -1;
279
        old = -1;
280
        rn = "invalid";
281
        goto print;
282
    }
283
    switch (reg) {
284
    case 0:
285
        val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
286
        old = env->CP0_index;
287
        env->CP0_index = val;
288
        rn = "Index";
289
        break;
290
    case 2:
291
        val = T0 & 0x03FFFFFFF;
292
        old = env->CP0_EntryLo0;
293
        env->CP0_EntryLo0 = val;
294
        rn = "EntryLo0";
295
        break;
296
    case 3:
297
        val = T0 & 0x03FFFFFFF;
298
        old = env->CP0_EntryLo1;
299
        env->CP0_EntryLo1 = val;
300
        rn = "EntryLo1";
301
        break;
302
    case 4:
303
        val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
304
        old = env->CP0_Context;
305
        env->CP0_Context = val;
306
        rn = "Context";
307
        break;
308
    case 5:
309
        val = T0 & 0x01FFE000;
310
        old = env->CP0_PageMask;
311
        env->CP0_PageMask = val;
312
        rn = "PageMask";
313
        break;
314
    case 6:
315
        val = T0 & 0x0000000F;
316
        old = env->CP0_Wired;
317
        env->CP0_Wired = val;
318
        rn = "Wired";
319
        break;
320
    case 9:
321
        val = T0;
322
        old = cpu_mips_get_count(env);
323
        cpu_mips_store_count(env, val);
324
        rn = "Count";
325
        break;
326
    case 10:
327
        val = T0 & 0xFFFFF0FF;
328
        old = env->CP0_EntryHi;
329
        env->CP0_EntryHi = val;
330
        rn = "EntryHi";
331
        break;
332
    case 11:
333
        val = T0;
334
        old = env->CP0_Compare;
335
        cpu_mips_store_compare(env, val);
336
        rn = "Compare";
337
        break;
338
    case 12:
339
        val = T0 & 0xFA78FF01;
340
        if (T0 & (1 << CP0St_UM))
341
            env->hflags |= MIPS_HFLAG_UM;
342
        else
343
            env->hflags &= ~MIPS_HFLAG_UM;
344
        if (T0 & (1 << CP0St_ERL))
345
            env->hflags |= MIPS_HFLAG_ERL;
346
        else
347
            env->hflags &= ~MIPS_HFLAG_ERL;
348
        if (T0 & (1 << CP0St_EXL))
349
            env->hflags |= MIPS_HFLAG_EXL;
350
        else
351
            env->hflags &= ~MIPS_HFLAG_EXL;
352
        old = env->CP0_Status;
353
        env->CP0_Status = val;
354
        /* If we unmasked an asserted IRQ, raise it */
355
        mask = 0x0000FC00;
356
        if (loglevel & CPU_LOG_TB_IN_ASM) {
357
            fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
358
                    old, val, env->CP0_Cause, old & mask, val & mask,
359
                    env->CP0_Cause & mask);
360
        }
361
#if 1
362
        if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
363
            !(env->hflags & MIPS_HFLAG_EXL) &&
364
            !(env->hflags & MIPS_HFLAG_ERL) &&
365
            !(env->hflags & MIPS_HFLAG_DM) && 
366
            (env->CP0_Cause & mask)) {
367
            if (logfile)
368
                fprintf(logfile, "Raise pending IRQs\n");
369
            env->interrupt_request |= CPU_INTERRUPT_HARD;
370
            do_raise_exception(EXCP_EXT_INTERRUPT);
371
        } else if (!(val & 0x00000001) && (old & 0x00000001)) {
372
            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
373
        }
374
#endif
375
        rn = "Status";
376
        break;
377
    case 13:
378
        val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
379
        old = env->CP0_Cause;
380
        env->CP0_Cause = val;
381
#if 0
382
        /* Check if we ever asserted a software IRQ */
383
        for (i = 0; i < 2; i++) {
384
            mask = 0x100 << i;
385
            if ((val & mask) & !(old & mask))
386
                mips_set_irq(i);
387
        }
388
#endif
389
        rn = "Cause";
390
        break;
391
    case 14:
392
        val = T0;
393
        old = env->CP0_EPC;
394
        env->CP0_EPC = val;
395
        rn = "EPC";
396
        break;
397
    case 16:
398
        switch (sel) {
399
        case 0:
400
#if defined(MIPS_USES_R4K_TLB)
401
            val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
402
#else
403
            val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
404
#endif
405
            old = env->CP0_Config0;
406
            env->CP0_Config0 = val;
407
            rn = "Config0";
408
            break;
409
        default:
410
            val = -1;
411
            old = -1;
412
            rn = "bad config selector";
413
            break;
414
        }
415
        break;
416
    case 18:
417
        val = T0;
418
        old = env->CP0_WatchLo;
419
        env->CP0_WatchLo = val;
420
        rn = "WatchLo";
421
        break;
422
    case 19:
423
        val = T0 & 0x40FF0FF8;
424
        old = env->CP0_WatchHi;
425
        env->CP0_WatchHi = val;
426
        rn = "WatchHi";
427
        break;
428
    case 23:
429
        val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
430
        if (T0 & (1 << CP0DB_DM))
431
            env->hflags |= MIPS_HFLAG_DM;
432
        else
433
            env->hflags &= ~MIPS_HFLAG_DM;
434
        old = env->CP0_Debug;
435
        env->CP0_Debug = val;
436
        rn = "Debug";
437
        break;
438
    case 24:
439
        val = T0;
440
        old = env->CP0_DEPC;
441
        env->CP0_DEPC = val;
442
        rn = "DEPC";
443
        break;
444
    case 28:
445
        switch (sel) {
446
        case 0:
447
            val = T0 & 0xFFFFFCF6;
448
            old = env->CP0_TagLo;
449
            env->CP0_TagLo = val;
450
            rn = "TagLo";
451
            break;
452
        default:
453
            val = -1;
454
            old = -1;
455
            rn = "invalid sel";
456
            break;
457
        }
458
        break;
459
    case 30:
460
        val = T0;
461
        old = env->CP0_ErrorEPC;
462
        env->CP0_ErrorEPC = val;
463
        rn = "EPC";
464
        break;
465
    case 31:
466
        val = T0;
467
        old = env->CP0_DESAVE;
468
        env->CP0_DESAVE = val;
469
        rn = "DESAVE";
470
        break;
471
    default:
472
        val = -1;
473
        old = -1;
474
        rn = "unknown";
475
        break;
476
    }
477
 print:
478
#if defined MIPS_DEBUG_DISAS
479
    if (loglevel & CPU_LOG_TB_IN_ASM) {
480
        fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
481
                env->PC, rn, T0, val, reg, sel, old);
482
    }
483
#endif
484
    return;
485
}
486

    
487
/* TLB management */
488
#if defined(MIPS_USES_R4K_TLB)
489
__attribute__ (( regparm(1) ))
490
static void invalidate_tb (int idx)
491
{
492
    tlb_t *tlb;
493
    target_ulong addr, end;
494

    
495
    tlb = &env->tlb[idx];
496
    if (tlb->V[0]) {
497
        addr = tlb->PFN[0];
498
        end = addr + (tlb->end - tlb->VPN);
499
        tb_invalidate_page_range(addr, end);
500
    }
501
    if (tlb->V[1]) {
502
        addr = tlb->PFN[1];
503
        end = addr + (tlb->end - tlb->VPN);
504
        tb_invalidate_page_range(addr, end);
505
    }
506
}
507

    
508
__attribute__ (( regparm(1) ))
509
static void fill_tb (int idx)
510
{
511
    tlb_t *tlb;
512
    int size;
513

    
514
    /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
515
    tlb = &env->tlb[idx];
516
    tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
517
    tlb->ASID = env->CP0_EntryHi & 0x000000FF;
518
    size = env->CP0_PageMask >> 13;
519
    size = 4 * (size + 1);
520
    tlb->end = tlb->VPN + (1 << (8 + size));
521
    tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
522
    tlb->V[0] = env->CP0_EntryLo0 & 2;
523
    tlb->D[0] = env->CP0_EntryLo0 & 4;
524
    tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7;
525
    tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
526
    tlb->V[1] = env->CP0_EntryLo1 & 2;
527
    tlb->D[1] = env->CP0_EntryLo1 & 4;
528
    tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7;
529
    tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
530
}
531

    
532
void do_tlbwi (void)
533
{
534
    invalidate_tb(env->CP0_index & 0xF);
535
    fill_tb(env->CP0_index & 0xF);
536
}
537

    
538
void do_tlbwr (void)
539
{
540
    int r = cpu_mips_get_random(env);
541

    
542
    invalidate_tb(r);
543
    fill_tb(r);
544
}
545

    
546
void do_tlbp (void)
547
{
548
    tlb_t *tlb;
549
    target_ulong tag;
550
    uint8_t ASID;
551
    int i;
552

    
553
    tag = (env->CP0_EntryHi & 0xFFFFE000);
554
    ASID = env->CP0_EntryHi & 0x000000FF;
555
        for (i = 0; i < 16; i++) {
556
        tlb = &env->tlb[i];
557
        /* Check ASID, virtual page number & size */
558
        if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
559
            /* TLB match */
560
            env->CP0_index = i;
561
            break;
562
        }
563
    }
564
    if (i == 16) {
565
        env->CP0_index |= 0x80000000;
566
    }
567
}
568

    
569
void do_tlbr (void)
570
{
571
    tlb_t *tlb;
572
    int size;
573

    
574
    tlb = &env->tlb[env->CP0_index & 0xF];
575
    env->CP0_EntryHi = tlb->VPN | tlb->ASID;
576
    size = (tlb->end - tlb->VPN) >> 12;
577
    env->CP0_PageMask = (size - 1) << 13;
578
    env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
579
        (tlb->PFN[0] >> 6);
580
    env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
581
        (tlb->PFN[1] >> 6);
582
}
583
#endif
584

    
585
__attribute__ (( regparm(1) ))
586
void op_dump_ldst (const unsigned char *func)
587
{
588
    if (loglevel)
589
        fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
590
}
591

    
592
void dump_sc (void)
593
{
594
    if (loglevel) {
595
        fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
596
                T1, T0, env->CP0_LLAddr);
597
    }
598
}
599

    
600
void debug_eret (void)
601
{
602
    if (loglevel) {
603
        fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
604
                env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
605
                env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
606
    }
607
}
608

    
609
__attribute__ (( regparm(1) ))
610
void do_pmon (int function)
611
{
612
    function /= 2;
613
    switch (function) {
614
    case 2: /* TODO: char inbyte(int waitflag); */
615
        if (env->gpr[4] == 0)
616
            env->gpr[2] = -1;
617
        /* Fall through */
618
    case 11: /* TODO: char inbyte (void); */
619
        env->gpr[2] = -1;
620
        break;
621
    case 3:
622
    case 12:
623
        printf("%c", env->gpr[4] & 0xFF);
624
        break;
625
    case 17:
626
        break;
627
    case 158:
628
        {
629
            unsigned char *fmt = (void *)env->gpr[4];
630
            printf("%s", fmt);
631
        }
632
        break;
633
    }
634
}