399200865d0d341406c87f2a355cbb49e54e35e5
[qemu] / target-mips / op_helper.c
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 |= (1 << CP0St_UM);
178         if (env->hflags & MIPS_HFLAG_ERL)
179             T0 |= (1 << CP0St_ERL);
180         if (env->hflags & MIPS_HFLAG_EXL)
181             T0 |= (1 << 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 = 0x0000FF00;
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_Status & 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     /* Wildly undefined effects for CP0_index containing a too high value and
535        MIPS_TLB_NB not being a power of two.  But so does real silicon.  */
536     invalidate_tb(env->CP0_index & (MIPS_TLB_NB - 1));
537     fill_tb(env->CP0_index & (MIPS_TLB_NB - 1));
538 }
539
540 void do_tlbwr (void)
541 {
542     int r = cpu_mips_get_random(env);
543
544     invalidate_tb(r);
545     fill_tb(r);
546 }
547
548 void do_tlbp (void)
549 {
550     tlb_t *tlb;
551     target_ulong tag;
552     uint8_t ASID;
553     int i;
554
555     tag = (env->CP0_EntryHi & 0xFFFFE000);
556     ASID = env->CP0_EntryHi & 0x000000FF;
557         for (i = 0; i < MIPS_TLB_NB; i++) {
558         tlb = &env->tlb[i];
559         /* Check ASID, virtual page number & size */
560         if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
561             /* TLB match */
562             env->CP0_index = i;
563             break;
564         }
565     }
566     if (i == MIPS_TLB_NB) {
567         env->CP0_index |= 0x80000000;
568     }
569 }
570
571 void do_tlbr (void)
572 {
573     tlb_t *tlb;
574     int size;
575
576     tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
577     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
578     size = (tlb->end - tlb->VPN) >> 12;
579     env->CP0_PageMask = (size - 1) << 13;
580     env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
581         (tlb->PFN[0] >> 6);
582     env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
583         (tlb->PFN[1] >> 6);
584 }
585 #endif
586
587 __attribute__ (( regparm(1) ))
588 void op_dump_ldst (const unsigned char *func)
589 {
590     if (loglevel)
591         fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
592 }
593
594 void dump_sc (void)
595 {
596     if (loglevel) {
597         fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
598                 T1, T0, env->CP0_LLAddr);
599     }
600 }
601
602 void debug_eret (void)
603 {
604     if (loglevel) {
605         fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
606                 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
607                 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
608     }
609 }
610
611 __attribute__ (( regparm(1) ))
612 void do_pmon (int function)
613 {
614     function /= 2;
615     switch (function) {
616     case 2: /* TODO: char inbyte(int waitflag); */
617         if (env->gpr[4] == 0)
618             env->gpr[2] = -1;
619         /* Fall through */
620     case 11: /* TODO: char inbyte (void); */
621         env->gpr[2] = -1;
622         break;
623     case 3:
624     case 12:
625         printf("%c", env->gpr[4] & 0xFF);
626         break;
627     case 17:
628         break;
629     case 158:
630         {
631             unsigned char *fmt = (void *)env->gpr[4];
632             printf("%s", fmt);
633         }
634         break;
635     }
636 }