]> git.proxmox.com Git - mirror_qemu.git/blame - target-mips/op_helper.c
report C0 status correctly (Ralf Baechle)
[mirror_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
32__attribute__ (( regparm(2) ))
33void 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) ))
46void 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)
65static inline uint64_t get_HILO (void)
66{
67 return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
68}
69
70static inline void set_HILO (uint64_t HILO)
71{
72 env->LO = HILO & 0xFFFFFFFF;
73 env->HI = HILO >> 32;
74}
75
76void do_mult (void)
77{
78 set_HILO((int64_t)T0 * (int64_t)T1);
79}
80
81void do_multu (void)
82{
83 set_HILO((uint64_t)T0 * (uint64_t)T1);
84}
85
86void 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
94void 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
102void 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
110void 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) ))
121void 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)
90b37806 177 T0 |= (1 << CP0St_UM);
6af0bf9c 178 if (env->hflags & MIPS_HFLAG_ERL)
90b37806 179 T0 |= (1 << CP0St_ERL);
6af0bf9c 180 if (env->hflags & MIPS_HFLAG_EXL)
90b37806 181 T0 |= (1 << CP0St_EXL);
6af0bf9c
FB
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) ))
271void 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 */
ae022501 355 mask = 0x0000FF00;
6af0bf9c
FB
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) ))
490static 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) ))
509static 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
532void do_tlbwi (void)
533{
534 invalidate_tb(env->CP0_index & 0xF);
535 fill_tb(env->CP0_index & 0xF);
536}
537
538void do_tlbwr (void)
539{
540 int r = cpu_mips_get_random(env);
541
542 invalidate_tb(r);
543 fill_tb(r);
544}
545
546void 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
569void 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) ))
586void op_dump_ldst (const unsigned char *func)
587{
588 if (loglevel)
589 fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
590}
591
592void 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
600void 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) ))
610void 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}