Fix keyboard detection bugs
[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 <stdlib.h>
21 #include "exec.h"
22
23 #define GETPC() (__builtin_return_address(0))
24
25 /*****************************************************************************/
26 /* Exceptions processing helpers */
27 void cpu_loop_exit(void)
28 {
29     longjmp(env->jmp_env, 1);
30 }
31
32 void do_raise_exception_err (uint32_t exception, int error_code)
33 {
34 #if 1
35     if (logfile && exception < 0x100)
36         fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
37 #endif
38     env->exception_index = exception;
39     env->error_code = error_code;
40     T0 = 0;
41     cpu_loop_exit();
42 }
43
44 void do_raise_exception (uint32_t exception)
45 {
46     do_raise_exception_err(exception, 0);
47 }
48
49 void do_restore_state (void *pc_ptr)
50 {
51   TranslationBlock *tb;
52   unsigned long pc = (unsigned long) pc_ptr;
53
54   tb = tb_find_pc (pc);
55   cpu_restore_state (tb, env, pc, NULL);
56 }
57
58 void do_raise_exception_direct_err (uint32_t exception, int error_code)
59 {
60     do_restore_state (GETPC ());
61     do_raise_exception_err (exception, error_code);
62 }
63
64 void do_raise_exception_direct (uint32_t exception)
65 {
66     do_raise_exception_direct_err (exception, 0);
67 }
68
69 #define MEMSUFFIX _raw
70 #include "op_helper_mem.c"
71 #undef MEMSUFFIX
72 #if !defined(CONFIG_USER_ONLY)
73 #define MEMSUFFIX _user
74 #include "op_helper_mem.c"
75 #undef MEMSUFFIX
76 #define MEMSUFFIX _kernel
77 #include "op_helper_mem.c"
78 #undef MEMSUFFIX
79 #endif
80
81 #ifdef TARGET_MIPS64
82 #if TARGET_LONG_BITS > HOST_LONG_BITS
83 /* Those might call libgcc functions.  */
84 void do_dsll (void)
85 {
86     T0 = T0 << T1;
87 }
88
89 void do_dsll32 (void)
90 {
91     T0 = T0 << (T1 + 32);
92 }
93
94 void do_dsra (void)
95 {
96     T0 = (int64_t)T0 >> T1;
97 }
98
99 void do_dsra32 (void)
100 {
101     T0 = (int64_t)T0 >> (T1 + 32);
102 }
103
104 void do_dsrl (void)
105 {
106     T0 = T0 >> T1;
107 }
108
109 void do_dsrl32 (void)
110 {
111     T0 = T0 >> (T1 + 32);
112 }
113
114 void do_drotr (void)
115 {
116     target_ulong tmp;
117
118     if (T1) {
119        tmp = T0 << (0x40 - T1);
120        T0 = (T0 >> T1) | tmp;
121     }
122 }
123
124 void do_drotr32 (void)
125 {
126     target_ulong tmp;
127
128     if (T1) {
129        tmp = T0 << (0x40 - (32 + T1));
130        T0 = (T0 >> (32 + T1)) | tmp;
131     }
132 }
133
134 void do_dsllv (void)
135 {
136     T0 = T1 << (T0 & 0x3F);
137 }
138
139 void do_dsrav (void)
140 {
141     T0 = (int64_t)T1 >> (T0 & 0x3F);
142 }
143
144 void do_dsrlv (void)
145 {
146     T0 = T1 >> (T0 & 0x3F);
147 }
148
149 void do_drotrv (void)
150 {
151     target_ulong tmp;
152
153     T0 &= 0x3F;
154     if (T0) {
155        tmp = T1 << (0x40 - T0);
156        T0 = (T1 >> T0) | tmp;
157     } else
158        T0 = T1;
159 }
160 #endif /* TARGET_LONG_BITS > HOST_LONG_BITS */
161 #endif /* TARGET_MIPS64 */
162
163 /* 64 bits arithmetic for 32 bits hosts */
164 #if TARGET_LONG_BITS > HOST_LONG_BITS
165 static inline uint64_t get_HILO (void)
166 {
167     return (env->HI << 32) | (uint32_t)env->LO;
168 }
169
170 static inline void set_HILO (uint64_t HILO)
171 {
172     env->LO = (int32_t)HILO;
173     env->HI = (int32_t)(HILO >> 32);
174 }
175
176 void do_mult (void)
177 {
178     set_HILO((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
179 }
180
181 void do_multu (void)
182 {
183     set_HILO((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
184 }
185
186 void do_madd (void)
187 {
188     int64_t tmp;
189
190     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
191     set_HILO((int64_t)get_HILO() + tmp);
192 }
193
194 void do_maddu (void)
195 {
196     uint64_t tmp;
197
198     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
199     set_HILO(get_HILO() + tmp);
200 }
201
202 void do_msub (void)
203 {
204     int64_t tmp;
205
206     tmp = ((int64_t)(int32_t)T0 * (int64_t)(int32_t)T1);
207     set_HILO((int64_t)get_HILO() - tmp);
208 }
209
210 void do_msubu (void)
211 {
212     uint64_t tmp;
213
214     tmp = ((uint64_t)(uint32_t)T0 * (uint64_t)(uint32_t)T1);
215     set_HILO(get_HILO() - tmp);
216 }
217 #endif
218
219 #if HOST_LONG_BITS < 64
220 void do_div (void)
221 {
222     /* 64bit datatypes because we may see overflow/underflow. */
223     if (T1 != 0) {
224         env->LO = (int32_t)((int64_t)(int32_t)T0 / (int32_t)T1);
225         env->HI = (int32_t)((int64_t)(int32_t)T0 % (int32_t)T1);
226     }
227 }
228 #endif
229
230 #ifdef TARGET_MIPS64
231 void do_dmult (void)
232 {
233     env->LO = (int64_t)T0 * (int64_t)T1;
234     /* XXX */
235     env->HI = (env->LO | (1ULL << 63)) ? ~0ULL : 0ULL;
236 }
237
238 void do_dmultu (void)
239 {
240     env->LO = T0 * T1;
241     /* XXX */
242     env->HI = 0;
243 }
244
245 void do_ddiv (void)
246 {
247     if (T1 != 0) {
248         lldiv_t res = lldiv((int64_t)T0, (int64_t)T1);
249         env->LO = res.quot;
250         env->HI = res.rem;
251     }
252 }
253
254 void do_ddivu (void)
255 {
256     if (T1 != 0) {
257         /* XXX: lldivu? */
258         lldiv_t res = lldiv(T0, T1);
259         env->LO = (uint64_t)res.quot;
260         env->HI = (uint64_t)res.rem;
261     }
262 }
263 #endif
264
265 #if defined(CONFIG_USER_ONLY) 
266 void do_mfc0_random (void)
267 {
268     cpu_abort(env, "mfc0 random\n");
269 }
270
271 void do_mfc0_count (void)
272 {
273     cpu_abort(env, "mfc0 count\n");
274 }
275
276 void cpu_mips_store_count(CPUState *env, uint32_t value)
277 {
278     cpu_abort(env, "mtc0 count\n");
279 }
280
281 void cpu_mips_store_compare(CPUState *env, uint32_t value)
282 {
283     cpu_abort(env, "mtc0 compare\n");
284 }
285
286 void cpu_mips_update_irq(CPUState *env)
287 {
288     cpu_abort(env, "mtc0 status / mtc0 cause\n");
289 }
290
291 void do_mtc0_status_debug(uint32_t old, uint32_t val)
292 {
293     cpu_abort(env, "mtc0 status debug\n");
294 }
295
296 void do_mtc0_status_irqraise_debug (void)
297 {
298     cpu_abort(env, "mtc0 status irqraise debug\n");
299 }
300
301 void do_tlbwi (void)
302 {
303     cpu_abort(env, "tlbwi\n");
304 }
305
306 void do_tlbwr (void)
307 {
308     cpu_abort(env, "tlbwr\n");
309 }
310
311 void do_tlbp (void)
312 {
313     cpu_abort(env, "tlbp\n");
314 }
315
316 void do_tlbr (void)
317 {
318     cpu_abort(env, "tlbr\n");
319 }
320
321 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
322 {
323     cpu_abort(env, "mips_tlb_flush\n");
324 }
325
326 #else
327
328 /* CP0 helpers */
329 void do_mfc0_random (void)
330 {
331     T0 = (int32_t)cpu_mips_get_random(env);
332 }
333
334 void do_mfc0_count (void)
335 {
336     T0 = (int32_t)cpu_mips_get_count(env);
337 }
338
339 void do_mtc0_status_debug(uint32_t old, uint32_t val)
340 {
341     fprintf(logfile, "Status %08x (%08x) => %08x (%08x) Cause %08x",
342             old, old & env->CP0_Cause & CP0Ca_IP_mask,
343             val, val & env->CP0_Cause & CP0Ca_IP_mask,
344             env->CP0_Cause);
345     (env->hflags & MIPS_HFLAG_UM) ? fputs(", UM\n", logfile)
346                                   : fputs("\n", logfile);
347 }
348
349 void do_mtc0_status_irqraise_debug(void)
350 {
351     fprintf(logfile, "Raise pending IRQs\n");
352 }
353
354 void fpu_handle_exception(void)
355 {
356 #ifdef CONFIG_SOFTFLOAT
357     int flags = get_float_exception_flags(&env->fp_status);
358     unsigned int cpuflags = 0, enable, cause = 0;
359
360     enable = GET_FP_ENABLE(env->fcr31);
361
362     /* determine current flags */   
363     if (flags & float_flag_invalid) {
364         cpuflags |= FP_INVALID;
365         cause |= FP_INVALID & enable;
366     }
367     if (flags & float_flag_divbyzero) {
368         cpuflags |= FP_DIV0;    
369         cause |= FP_DIV0 & enable;
370     }
371     if (flags & float_flag_overflow) {
372         cpuflags |= FP_OVERFLOW;    
373         cause |= FP_OVERFLOW & enable;
374     }
375     if (flags & float_flag_underflow) {
376         cpuflags |= FP_UNDERFLOW;   
377         cause |= FP_UNDERFLOW & enable;
378     }
379     if (flags & float_flag_inexact) {
380         cpuflags |= FP_INEXACT; 
381         cause |= FP_INEXACT & enable;
382     }
383     SET_FP_FLAGS(env->fcr31, cpuflags);
384     SET_FP_CAUSE(env->fcr31, cause);
385 #else
386     SET_FP_FLAGS(env->fcr31, 0);
387     SET_FP_CAUSE(env->fcr31, 0);
388 #endif
389 }
390
391 /* TLB management */
392 #if defined(MIPS_USES_R4K_TLB)
393 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
394 {
395     /* Flush qemu's TLB and discard all shadowed entries.  */
396     tlb_flush (env, flush_global);
397     env->tlb_in_use = env->nb_tlb;
398 }
399
400 static void mips_tlb_flush_extra (CPUState *env, int first)
401 {
402     /* Discard entries from env->tlb[first] onwards.  */
403     while (env->tlb_in_use > first) {
404         invalidate_tlb(env, --env->tlb_in_use, 0);
405     }
406 }
407
408 static void fill_tlb (int idx)
409 {
410     tlb_t *tlb;
411
412     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
413     tlb = &env->tlb[idx];
414     tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF;
415     tlb->ASID = env->CP0_EntryHi & 0xFF;
416     tlb->PageMask = env->CP0_PageMask;
417     tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
418     tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
419     tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
420     tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
421     tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
422     tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
423     tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
424     tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
425     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
426 }
427
428 void do_tlbwi (void)
429 {
430     /* Discard cached TLB entries.  We could avoid doing this if the
431        tlbwi is just upgrading access permissions on the current entry;
432        that might be a further win.  */
433     mips_tlb_flush_extra (env, env->nb_tlb);
434
435     invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
436     fill_tlb(env->CP0_Index % env->nb_tlb);
437 }
438
439 void do_tlbwr (void)
440 {
441     int r = cpu_mips_get_random(env);
442
443     invalidate_tlb(env, r, 1);
444     fill_tlb(r);
445 }
446
447 void do_tlbp (void)
448 {
449     tlb_t *tlb;
450     target_ulong tag;
451     uint8_t ASID;
452     int i;
453
454     tag = env->CP0_EntryHi & (int32_t)0xFFFFE000;
455     ASID = env->CP0_EntryHi & 0xFF;
456     for (i = 0; i < env->nb_tlb; i++) {
457         tlb = &env->tlb[i];
458         /* Check ASID, virtual page number & size */
459         if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
460             /* TLB match */
461             env->CP0_Index = i;
462             break;
463         }
464     }
465     if (i == env->nb_tlb) {
466         /* No match.  Discard any shadow entries, if any of them match.  */
467         for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
468             tlb = &env->tlb[i];
469
470             /* Check ASID, virtual page number & size */
471             if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
472                 mips_tlb_flush_extra (env, i);
473                 break;
474             }
475         }
476
477         env->CP0_Index |= 0x80000000;
478     }
479 }
480
481 void do_tlbr (void)
482 {
483     tlb_t *tlb;
484     uint8_t ASID;
485
486     ASID = env->CP0_EntryHi & 0xFF;
487     tlb = &env->tlb[env->CP0_Index % env->nb_tlb];
488
489     /* If this will change the current ASID, flush qemu's TLB.  */
490     if (ASID != tlb->ASID)
491         cpu_mips_tlb_flush (env, 1);
492
493     mips_tlb_flush_extra(env, env->nb_tlb);
494
495     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
496     env->CP0_PageMask = tlb->PageMask;
497     env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
498                         (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
499     env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
500                         (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
501 }
502 #endif
503
504 #endif /* !CONFIG_USER_ONLY */
505
506 void dump_ldst (const unsigned char *func)
507 {
508     if (loglevel)
509         fprintf(logfile, "%s => " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, T0, T1);
510 }
511
512 void dump_sc (void)
513 {
514     if (loglevel) {
515         fprintf(logfile, "%s " TARGET_FMT_lx " at " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", __func__,
516                 T1, T0, env->CP0_LLAddr);
517     }
518 }
519
520 void debug_pre_eret (void)
521 {
522     fprintf(logfile, "ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
523             env->PC, env->CP0_EPC);
524     if (env->CP0_Status & (1 << CP0St_ERL))
525         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
526     if (env->hflags & MIPS_HFLAG_DM)
527         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
528     fputs("\n", logfile);
529 }
530
531 void debug_post_eret (void)
532 {
533     fprintf(logfile, "  =>  PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
534             env->PC, env->CP0_EPC);
535     if (env->CP0_Status & (1 << CP0St_ERL))
536         fprintf(logfile, " ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
537     if (env->hflags & MIPS_HFLAG_DM)
538         fprintf(logfile, " DEPC " TARGET_FMT_lx, env->CP0_DEPC);
539     if (env->hflags & MIPS_HFLAG_UM)
540         fputs(", UM\n", logfile);
541     else
542         fputs("\n", logfile);
543 }
544
545 void do_pmon (int function)
546 {
547     function /= 2;
548     switch (function) {
549     case 2: /* TODO: char inbyte(int waitflag); */
550         if (env->gpr[4] == 0)
551             env->gpr[2] = -1;
552         /* Fall through */
553     case 11: /* TODO: char inbyte (void); */
554         env->gpr[2] = -1;
555         break;
556     case 3:
557     case 12:
558         printf("%c", (char)(env->gpr[4] & 0xFF));
559         break;
560     case 17:
561         break;
562     case 158:
563         {
564             unsigned char *fmt = (void *)(unsigned long)env->gpr[4];
565             printf("%s", fmt);
566         }
567         break;
568     }
569 }
570
571 #if !defined(CONFIG_USER_ONLY) 
572
573 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
574
575 #define MMUSUFFIX _mmu
576 #define ALIGNED_ONLY
577
578 #define SHIFT 0
579 #include "softmmu_template.h"
580
581 #define SHIFT 1
582 #include "softmmu_template.h"
583
584 #define SHIFT 2
585 #include "softmmu_template.h"
586
587 #define SHIFT 3
588 #include "softmmu_template.h"
589
590 static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
591 {
592     env->CP0_BadVAddr = addr;
593     do_restore_state (retaddr);
594     do_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
595 }
596
597 void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
598 {
599     TranslationBlock *tb;
600     CPUState *saved_env;
601     unsigned long pc;
602     int ret;
603
604     /* XXX: hack to restore env in all cases, even if not called from
605        generated code */
606     saved_env = env;
607     env = cpu_single_env;
608     ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
609     if (ret) {
610         if (retaddr) {
611             /* now we have a real cpu fault */
612             pc = (unsigned long)retaddr;
613             tb = tb_find_pc(pc);
614             if (tb) {
615                 /* the PC is inside the translated code. It means that we have
616                    a virtual CPU fault */
617                 cpu_restore_state(tb, env, pc, NULL);
618             }
619         }
620         do_raise_exception_err(env->exception_index, env->error_code);
621     }
622     env = saved_env;
623 }
624
625 #endif