]> git.proxmox.com Git - qemu.git/blame - target-mips/op_helper.c
correct split between helper.c and op_helper.c - cosmetics
[qemu.git] / target-mips / op_helper.c
CommitLineData
6af0bf9c
FB
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 */
27void cpu_loop_exit(void)
28{
29 longjmp(env->jmp_env, 1);
30}
31
6af0bf9c
FB
32void 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
6af0bf9c
FB
44void do_raise_exception (uint32_t exception)
45{
46 do_raise_exception_err(exception, 0);
47}
48
49#define MEMSUFFIX _raw
50#include "op_helper_mem.c"
51#undef MEMSUFFIX
52#if !defined(CONFIG_USER_ONLY)
53#define MEMSUFFIX _user
54#include "op_helper_mem.c"
55#undef MEMSUFFIX
56#define MEMSUFFIX _kernel
57#include "op_helper_mem.c"
58#undef MEMSUFFIX
59#endif
60
61/* 64 bits arithmetic for 32 bits hosts */
62#if (HOST_LONG_BITS == 32)
63static inline uint64_t get_HILO (void)
64{
65 return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
66}
67
68static inline void set_HILO (uint64_t HILO)
69{
70 env->LO = HILO & 0xFFFFFFFF;
71 env->HI = HILO >> 32;
72}
73
74void do_mult (void)
75{
76 set_HILO((int64_t)T0 * (int64_t)T1);
77}
78
79void do_multu (void)
80{
81 set_HILO((uint64_t)T0 * (uint64_t)T1);
82}
83
84void do_madd (void)
85{
86 int64_t tmp;
87
88 tmp = ((int64_t)T0 * (int64_t)T1);
89 set_HILO((int64_t)get_HILO() + tmp);
90}
91
92void do_maddu (void)
93{
94 uint64_t tmp;
95
96 tmp = ((uint64_t)T0 * (uint64_t)T1);
97 set_HILO(get_HILO() + tmp);
98}
99
100void do_msub (void)
101{
102 int64_t tmp;
103
104 tmp = ((int64_t)T0 * (int64_t)T1);
105 set_HILO((int64_t)get_HILO() - tmp);
106}
107
108void do_msubu (void)
109{
110 uint64_t tmp;
111
112 tmp = ((uint64_t)T0 * (uint64_t)T1);
113 set_HILO(get_HILO() - tmp);
114}
115#endif
116
117/* CP0 helpers */
6af0bf9c
FB
118void do_mfc0 (int reg, int sel)
119{
120 const unsigned char *rn;
121
122 if (sel != 0 && reg != 16 && reg != 28) {
123 rn = "invalid";
124 goto print;
125 }
126 switch (reg) {
127 case 0:
128 T0 = env->CP0_index;
129 rn = "Index";
130 break;
131 case 1:
132 T0 = cpu_mips_get_random(env);
133 rn = "Random";
134 break;
135 case 2:
136 T0 = env->CP0_EntryLo0;
137 rn = "EntryLo0";
138 break;
139 case 3:
140 T0 = env->CP0_EntryLo1;
141 rn = "EntryLo1";
142 break;
143 case 4:
144 T0 = env->CP0_Context;
145 rn = "Context";
146 break;
147 case 5:
148 T0 = env->CP0_PageMask;
149 rn = "PageMask";
150 break;
151 case 6:
152 T0 = env->CP0_Wired;
153 rn = "Wired";
154 break;
155 case 8:
156 T0 = env->CP0_BadVAddr;
157 rn = "BadVaddr";
158 break;
159 case 9:
160 T0 = cpu_mips_get_count(env);
161 rn = "Count";
162 break;
163 case 10:
164 T0 = env->CP0_EntryHi;
165 rn = "EntryHi";
166 break;
167 case 11:
168 T0 = env->CP0_Compare;
169 rn = "Compare";
170 break;
171 case 12:
172 T0 = env->CP0_Status;
173 if (env->hflags & MIPS_HFLAG_UM)
90b37806 174 T0 |= (1 << CP0St_UM);
6af0bf9c 175 if (env->hflags & MIPS_HFLAG_ERL)
90b37806 176 T0 |= (1 << CP0St_ERL);
6af0bf9c 177 if (env->hflags & MIPS_HFLAG_EXL)
90b37806 178 T0 |= (1 << CP0St_EXL);
6af0bf9c
FB
179 rn = "Status";
180 break;
181 case 13:
182 T0 = env->CP0_Cause;
183 rn = "Cause";
184 break;
185 case 14:
186 T0 = env->CP0_EPC;
187 rn = "EPC";
188 break;
189 case 15:
190 T0 = env->CP0_PRid;
191 rn = "PRid";
192 break;
193 case 16:
194 switch (sel) {
195 case 0:
196 T0 = env->CP0_Config0;
197 rn = "Config";
198 break;
199 case 1:
200 T0 = env->CP0_Config1;
201 rn = "Config1";
202 break;
203 default:
204 rn = "Unknown config register";
205 break;
206 }
207 break;
208 case 17:
209 T0 = env->CP0_LLAddr >> 4;
210 rn = "LLAddr";
211 break;
212 case 18:
213 T0 = env->CP0_WatchLo;
214 rn = "WatchLo";
215 break;
216 case 19:
217 T0 = env->CP0_WatchHi;
218 rn = "WatchHi";
219 break;
220 case 23:
221 T0 = env->CP0_Debug;
222 if (env->hflags & MIPS_HFLAG_DM)
223 T0 |= 1 << CP0DB_DM;
224 rn = "Debug";
225 break;
226 case 24:
227 T0 = env->CP0_DEPC;
228 rn = "DEPC";
229 break;
230 case 28:
231 switch (sel) {
232 case 0:
233 T0 = env->CP0_TagLo;
234 rn = "TagLo";
235 break;
236 case 1:
237 T0 = env->CP0_DataLo;
238 rn = "DataLo";
239 break;
240 default:
241 rn = "unknown sel";
242 break;
243 }
244 break;
245 case 30:
246 T0 = env->CP0_ErrorEPC;
247 rn = "ErrorEPC";
248 break;
249 case 31:
250 T0 = env->CP0_DESAVE;
251 rn = "DESAVE";
252 break;
253 default:
254 rn = "unknown";
255 break;
256 }
257 print:
258#if defined MIPS_DEBUG_DISAS
259 if (loglevel & CPU_LOG_TB_IN_ASM) {
260 fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
261 env->PC, rn, T0, reg, sel);
262 }
263#endif
264 return;
265}
266
6af0bf9c
FB
267void do_mtc0 (int reg, int sel)
268{
269 const unsigned char *rn;
270 uint32_t val, old, mask;
6af0bf9c
FB
271
272 if (sel != 0 && reg != 16 && reg != 28) {
273 val = -1;
274 old = -1;
275 rn = "invalid";
276 goto print;
277 }
278 switch (reg) {
279 case 0:
280 val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
281 old = env->CP0_index;
282 env->CP0_index = val;
283 rn = "Index";
284 break;
285 case 2:
286 val = T0 & 0x03FFFFFFF;
287 old = env->CP0_EntryLo0;
288 env->CP0_EntryLo0 = val;
289 rn = "EntryLo0";
290 break;
291 case 3:
292 val = T0 & 0x03FFFFFFF;
293 old = env->CP0_EntryLo1;
294 env->CP0_EntryLo1 = val;
295 rn = "EntryLo1";
296 break;
297 case 4:
298 val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
299 old = env->CP0_Context;
300 env->CP0_Context = val;
301 rn = "Context";
302 break;
303 case 5:
304 val = T0 & 0x01FFE000;
305 old = env->CP0_PageMask;
306 env->CP0_PageMask = val;
307 rn = "PageMask";
308 break;
309 case 6:
310 val = T0 & 0x0000000F;
311 old = env->CP0_Wired;
312 env->CP0_Wired = val;
313 rn = "Wired";
314 break;
315 case 9:
316 val = T0;
317 old = cpu_mips_get_count(env);
318 cpu_mips_store_count(env, val);
319 rn = "Count";
320 break;
321 case 10:
322 val = T0 & 0xFFFFF0FF;
323 old = env->CP0_EntryHi;
324 env->CP0_EntryHi = val;
325 rn = "EntryHi";
326 break;
327 case 11:
328 val = T0;
329 old = env->CP0_Compare;
330 cpu_mips_store_compare(env, val);
331 rn = "Compare";
332 break;
333 case 12:
334 val = T0 & 0xFA78FF01;
335 if (T0 & (1 << CP0St_UM))
336 env->hflags |= MIPS_HFLAG_UM;
337 else
338 env->hflags &= ~MIPS_HFLAG_UM;
339 if (T0 & (1 << CP0St_ERL))
340 env->hflags |= MIPS_HFLAG_ERL;
341 else
342 env->hflags &= ~MIPS_HFLAG_ERL;
343 if (T0 & (1 << CP0St_EXL))
344 env->hflags |= MIPS_HFLAG_EXL;
345 else
346 env->hflags &= ~MIPS_HFLAG_EXL;
347 old = env->CP0_Status;
348 env->CP0_Status = val;
349 /* If we unmasked an asserted IRQ, raise it */
ae022501 350 mask = 0x0000FF00;
6af0bf9c
FB
351 if (loglevel & CPU_LOG_TB_IN_ASM) {
352 fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
353 old, val, env->CP0_Cause, old & mask, val & mask,
354 env->CP0_Cause & mask);
355 }
356#if 1
357 if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
358 !(env->hflags & MIPS_HFLAG_EXL) &&
359 !(env->hflags & MIPS_HFLAG_ERL) &&
360 !(env->hflags & MIPS_HFLAG_DM) &&
e1d9a508 361 (env->CP0_Status & env->CP0_Cause & mask)) {
6af0bf9c
FB
362 if (logfile)
363 fprintf(logfile, "Raise pending IRQs\n");
364 env->interrupt_request |= CPU_INTERRUPT_HARD;
365 do_raise_exception(EXCP_EXT_INTERRUPT);
366 } else if (!(val & 0x00000001) && (old & 0x00000001)) {
367 env->interrupt_request &= ~CPU_INTERRUPT_HARD;
368 }
369#endif
370 rn = "Status";
371 break;
372 case 13:
373 val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
374 old = env->CP0_Cause;
375 env->CP0_Cause = val;
376#if 0
e37e863f
FB
377 {
378 int i;
379 /* Check if we ever asserted a software IRQ */
380 for (i = 0; i < 2; i++) {
381 mask = 0x100 << i;
382 if ((val & mask) & !(old & mask))
383 mips_set_irq(i);
384 }
6af0bf9c
FB
385 }
386#endif
387 rn = "Cause";
388 break;
389 case 14:
390 val = T0;
391 old = env->CP0_EPC;
392 env->CP0_EPC = val;
393 rn = "EPC";
394 break;
395 case 16:
396 switch (sel) {
397 case 0:
398#if defined(MIPS_USES_R4K_TLB)
399 val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
400#else
401 val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
402#endif
403 old = env->CP0_Config0;
404 env->CP0_Config0 = val;
405 rn = "Config0";
406 break;
407 default:
408 val = -1;
409 old = -1;
410 rn = "bad config selector";
411 break;
412 }
413 break;
414 case 18:
415 val = T0;
416 old = env->CP0_WatchLo;
417 env->CP0_WatchLo = val;
418 rn = "WatchLo";
419 break;
420 case 19:
421 val = T0 & 0x40FF0FF8;
422 old = env->CP0_WatchHi;
423 env->CP0_WatchHi = val;
424 rn = "WatchHi";
425 break;
426 case 23:
427 val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
428 if (T0 & (1 << CP0DB_DM))
429 env->hflags |= MIPS_HFLAG_DM;
430 else
431 env->hflags &= ~MIPS_HFLAG_DM;
432 old = env->CP0_Debug;
433 env->CP0_Debug = val;
434 rn = "Debug";
435 break;
436 case 24:
437 val = T0;
438 old = env->CP0_DEPC;
439 env->CP0_DEPC = val;
440 rn = "DEPC";
441 break;
442 case 28:
443 switch (sel) {
444 case 0:
445 val = T0 & 0xFFFFFCF6;
446 old = env->CP0_TagLo;
447 env->CP0_TagLo = val;
448 rn = "TagLo";
449 break;
450 default:
451 val = -1;
452 old = -1;
453 rn = "invalid sel";
454 break;
455 }
456 break;
457 case 30:
458 val = T0;
459 old = env->CP0_ErrorEPC;
460 env->CP0_ErrorEPC = val;
461 rn = "EPC";
462 break;
463 case 31:
464 val = T0;
465 old = env->CP0_DESAVE;
466 env->CP0_DESAVE = val;
467 rn = "DESAVE";
468 break;
469 default:
470 val = -1;
471 old = -1;
472 rn = "unknown";
473 break;
474 }
475 print:
476#if defined MIPS_DEBUG_DISAS
477 if (loglevel & CPU_LOG_TB_IN_ASM) {
478 fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
479 env->PC, rn, T0, val, reg, sel, old);
480 }
481#endif
482 return;
483}
484
485/* TLB management */
486#if defined(MIPS_USES_R4K_TLB)
6af0bf9c
FB
487static void invalidate_tb (int idx)
488{
489 tlb_t *tlb;
490 target_ulong addr, end;
491
492 tlb = &env->tlb[idx];
493 if (tlb->V[0]) {
494 addr = tlb->PFN[0];
495 end = addr + (tlb->end - tlb->VPN);
496 tb_invalidate_page_range(addr, end);
497 }
498 if (tlb->V[1]) {
499 addr = tlb->PFN[1];
500 end = addr + (tlb->end - tlb->VPN);
501 tb_invalidate_page_range(addr, end);
502 }
503}
504
6af0bf9c
FB
505static void fill_tb (int idx)
506{
507 tlb_t *tlb;
508 int size;
509
510 /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
511 tlb = &env->tlb[idx];
512 tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
513 tlb->ASID = env->CP0_EntryHi & 0x000000FF;
514 size = env->CP0_PageMask >> 13;
515 size = 4 * (size + 1);
516 tlb->end = tlb->VPN + (1 << (8 + size));
517 tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
518 tlb->V[0] = env->CP0_EntryLo0 & 2;
519 tlb->D[0] = env->CP0_EntryLo0 & 4;
520 tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7;
521 tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
522 tlb->V[1] = env->CP0_EntryLo1 & 2;
523 tlb->D[1] = env->CP0_EntryLo1 & 4;
524 tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7;
525 tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
526}
527
528void do_tlbwi (void)
529{
7a962d30
FB
530 /* Wildly undefined effects for CP0_index containing a too high value and
531 MIPS_TLB_NB not being a power of two. But so does real silicon. */
532 invalidate_tb(env->CP0_index & (MIPS_TLB_NB - 1));
533 fill_tb(env->CP0_index & (MIPS_TLB_NB - 1));
6af0bf9c
FB
534}
535
536void do_tlbwr (void)
537{
538 int r = cpu_mips_get_random(env);
539
540 invalidate_tb(r);
541 fill_tb(r);
542}
543
544void do_tlbp (void)
545{
546 tlb_t *tlb;
547 target_ulong tag;
548 uint8_t ASID;
549 int i;
550
551 tag = (env->CP0_EntryHi & 0xFFFFE000);
552 ASID = env->CP0_EntryHi & 0x000000FF;
7a962d30 553 for (i = 0; i < MIPS_TLB_NB; i++) {
6af0bf9c
FB
554 tlb = &env->tlb[i];
555 /* Check ASID, virtual page number & size */
556 if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
557 /* TLB match */
558 env->CP0_index = i;
559 break;
560 }
561 }
7a962d30 562 if (i == MIPS_TLB_NB) {
6af0bf9c
FB
563 env->CP0_index |= 0x80000000;
564 }
565}
566
567void do_tlbr (void)
568{
569 tlb_t *tlb;
570 int size;
571
7a962d30 572 tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
6af0bf9c
FB
573 env->CP0_EntryHi = tlb->VPN | tlb->ASID;
574 size = (tlb->end - tlb->VPN) >> 12;
575 env->CP0_PageMask = (size - 1) << 13;
576 env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
577 (tlb->PFN[0] >> 6);
578 env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
579 (tlb->PFN[1] >> 6);
580}
581#endif
582
6af0bf9c
FB
583void op_dump_ldst (const unsigned char *func)
584{
585 if (loglevel)
586 fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
587}
588
589void dump_sc (void)
590{
591 if (loglevel) {
592 fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
593 T1, T0, env->CP0_LLAddr);
594 }
595}
596
597void debug_eret (void)
598{
599 if (loglevel) {
600 fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
601 env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
602 env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
603 }
604}
605
6af0bf9c
FB
606void do_pmon (int function)
607{
608 function /= 2;
609 switch (function) {
610 case 2: /* TODO: char inbyte(int waitflag); */
611 if (env->gpr[4] == 0)
612 env->gpr[2] = -1;
613 /* Fall through */
614 case 11: /* TODO: char inbyte (void); */
615 env->gpr[2] = -1;
616 break;
617 case 3:
618 case 12:
619 printf("%c", env->gpr[4] & 0xFF);
620 break;
621 case 17:
622 break;
623 case 158:
624 {
625 unsigned char *fmt = (void *)env->gpr[4];
626 printf("%s", fmt);
627 }
628 break;
629 }
630}
e37e863f
FB
631
632#if !defined(CONFIG_USER_ONLY)
633
634#define MMUSUFFIX _mmu
635#define GETPC() (__builtin_return_address(0))
636
637#define SHIFT 0
638#include "softmmu_template.h"
639
640#define SHIFT 1
641#include "softmmu_template.h"
642
643#define SHIFT 2
644#include "softmmu_template.h"
645
646#define SHIFT 3
647#include "softmmu_template.h"
648
649void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
650{
651 TranslationBlock *tb;
652 CPUState *saved_env;
653 unsigned long pc;
654 int ret;
655
656 /* XXX: hack to restore env in all cases, even if not called from
657 generated code */
658 saved_env = env;
659 env = cpu_single_env;
660 ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
661 if (ret) {
662 if (retaddr) {
663 /* now we have a real cpu fault */
664 pc = (unsigned long)retaddr;
665 tb = tb_find_pc(pc);
666 if (tb) {
667 /* the PC is inside the translated code. It means that we have
668 a virtual CPU fault */
669 cpu_restore_state(tb, env, pc, NULL);
670 }
671 }
672 do_raise_exception_err(env->exception_index, env->error_code);
673 }
674 env = saved_env;
675}
676
677#endif