003d252c1e93482f6144bb13a3301ecb32fa42e9
[projects/cmucl/cmucl.git] / src / lisp / mips-arch.c
1 /*
2
3  $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/mips-arch.c,v 1.12 2008/03/19 09:17:13 cshapiro Rel $
4
5  This code was written as part of the CMU Common Lisp project at
6  Carnegie Mellon University, and has been placed in the public domain.
7
8 */
9
10 #include <stdio.h>
11
12 #ifdef mach
13 #include <mips/cpu.h>
14 #else
15 #ifdef irix
16 #include <sys/sbd.h>
17 #endif
18 #endif
19
20 #include "lisp.h"
21 #include "globals.h"
22 #include "validate.h"
23 #include "os.h"
24 #include "internals.h"
25 #include "arch.h"
26 #include "lispregs.h"
27 #include "signal.h"
28 #include "alloc.h"
29 #include "interrupt.h"
30 #include "interr.h"
31 #include "breakpoint.h"
32
33 char *
34 arch_init(void)
35 {
36     return NULL;
37 }
38
39 os_vm_address_t
40 arch_get_bad_addr(int sig, int code, struct sigcontext * scp)
41 {
42     /* Finding the bad address on the mips is easy. */
43     return (os_vm_address_t) scp->sc_badvaddr;
44 }
45
46 #ifdef irix
47 void
48 emulate_branch(struct sigcontext *scp, unsigned long inst)
49 {
50     long opcode = inst >> 26;
51     long r1 = (inst >> 21) & 0x1f;
52     long r2 = (inst >> 16) & 0x1f;
53     long bdisp = (inst & (1 << 15)) ? inst | (-1 << 16) : inst & 0xffff;
54     long jdisp = (inst & (1 << 25)) ? inst | (-1 << 26) : inst & 0xffff;
55     long disp = 0;
56
57     switch (opcode) {
58       case 0x1:         /* bltz, bgez, bltzal, bgezal */
59           switch ((inst >> 16) & 0x1f) {
60             case 0x00:          /* bltz */
61                 if (scp->sc_regs[r1] < 0)
62                     disp = bdisp;
63                 break;
64             case 0x01:          /* bgez */
65                 if (scp->sc_regs[r1] >= 0)
66                     disp = bdisp;
67                 break;
68             case 0x10:          /* bltzal */
69                 if (scp->sc_regs[r1] < 0)
70                     disp = bdisp;
71                 scp->sc_regs[31] = scp->sc_pc + 4;
72                 break;
73             case 0x11:          /* bgezal */
74                 if (scp->sc_regs[r1] >= 0)
75                     disp = bdisp;
76                 scp->sc_regs[31] = scp->sc_pc + 4;
77                 break;
78           }
79           break;
80       case 0x4:         /* beq */
81           if (scp->sc_regs[r1] == scp->sc_regs[r2])
82               disp = bdisp;
83           break;
84       case 0x5:         /* bne */
85           if (scp->sc_regs[r1] != scp->sc_regs[r2])
86               disp = bdisp;
87           break;
88       case 0x6:         /* ble */
89           if (scp->sc_regs[r1] <= scp->sc_regs[r2])
90               disp = bdisp;
91           break;
92       case 0x7:         /* bgtz */
93           if (scp->sc_regs[r1] >= scp->sc_regs[r2])
94               disp = bdisp;
95           break;
96       case 0x2:         /* j */
97           disp = jdisp;
98           break;
99       case 0x3:         /* jal */
100           disp = jdisp;
101           scp->sc_regs[31] = scp->sc_pc + 4;
102           break;
103     }
104     scp->sc_pc += disp * 4;
105 }
106 #endif
107
108 void
109 arch_skip_instruction(scp)
110      struct sigcontext *scp;
111 {
112     /* Skip the offending instruction */
113     if (scp->sc_cause & CAUSE_BD)
114         emulate_branch(scp, *(unsigned long *) scp->sc_pc);
115     else
116         scp->sc_pc += 4;
117 }
118
119 unsigned char *
120 arch_internal_error_arguments(struct sigcontext *scp)
121 {
122     if (scp->sc_cause & CAUSE_BD)
123         return (unsigned char *) (scp->sc_pc + 8);
124     else
125         return (unsigned char *) (scp->sc_pc + 4);
126 }
127
128 boolean
129 arch_pseudo_atomic_atomic(struct sigcontext *scp)
130 {
131     return (scp->sc_regs[reg_ALLOC] & 1);
132 }
133
134 #define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
135
136 void
137 arch_set_pseudo_atomic_interrupted(struct sigcontext *scp)
138 {
139     scp->sc_regs[reg_NL4] += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
140 }
141
142 unsigned long
143 arch_install_breakpoint(void *pc)
144 {
145     unsigned long *ptr = (unsigned long *) pc;
146     unsigned long result = *ptr;
147
148     *ptr = (trap_Breakpoint << 16) | 0xd;
149
150     os_flush_icache((os_vm_address_t) ptr, sizeof(unsigned long));
151
152     return result;
153 }
154
155 void
156 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
157 {
158     *(unsigned long *) pc = orig_inst;
159
160     os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
161 }
162
163 static unsigned long *skipped_break_addr, displaced_after_inst;
164 static int orig_sigmask;
165
166 void
167 arch_do_displaced_inst(struct sigcontext *scp, unsigned long orig_inst)
168 {
169     unsigned long *pc = (unsigned long *) scp->sc_pc;
170     unsigned long *break_pc, *next_pc;
171     unsigned long next_inst;
172     int opcode;
173     struct sigcontext tmp;
174
175     orig_sigmask = scp->sc_mask;
176     scp->sc_mask = BLOCKABLE;
177
178     /* Figure out where the breakpoint is, and what happens next. */
179     if (scp->sc_cause & CAUSE_BD) {
180         break_pc = pc + 1;
181         next_inst = *pc;
182     } else {
183         break_pc = pc;
184         next_inst = orig_inst;
185     }
186
187     /* Put the original instruction back. */
188     *break_pc = orig_inst;
189     os_flush_icache((os_vm_address_t) break_pc, sizeof(unsigned long));
190
191     skipped_break_addr = break_pc;
192
193     /* Figure out where it goes. */
194     opcode = next_inst >> 26;
195     if (opcode == 1 || ((opcode & 0x3c) == 0x4)
196         || ((next_inst & 0xf00e0000) == 0x80000000)) {
197         tmp = *scp;
198         emulate_branch(&tmp, next_inst);
199         next_pc = (unsigned long *) tmp.sc_pc;
200     } else
201         next_pc = pc + 1;
202
203     displaced_after_inst = *next_pc;
204     *next_pc = (trap_AfterBreakpoint << 16) | 0xd;
205     os_flush_icache((os_vm_address_t) next_pc, sizeof(unsigned long));
206
207 #ifdef mach
208     sigreturn(scp);
209 #endif
210 }
211
212 static void
213 sigtrap_handler(int signal, int code, struct sigcontext *scp)
214 {
215     /* Don't disallow recursive breakpoint traps.  Otherwise, we can't */
216     /* use debugger breakpoints anywhere in here. */
217     sigsetmask(scp->sc_mask);
218
219     switch (code) {
220       case trap_PendingInterrupt:
221           arch_skip_instruction(scp);
222           interrupt_handle_pending(scp);
223           break;
224
225       case trap_Halt:
226           fake_foreign_function_call(scp);
227           lose("%%primitive halt called; the party is over.\n");
228
229       case trap_Error:
230       case trap_Cerror:
231           interrupt_internal_error(signal, code, scp, code == trap_Cerror);
232           break;
233
234       case trap_Breakpoint:
235           handle_breakpoint(signal, code, scp);
236           break;
237
238       case trap_FunctionEndBreakpoint:
239           scp->sc_pc = (int) handle_function_end_breakpoint(signal, code, scp);
240           break;
241
242       case trap_AfterBreakpoint:
243           *skipped_break_addr = (trap_Breakpoint << 16) | 0xd;
244           os_flush_icache((os_vm_address_t) skipped_break_addr,
245
246                           sizeof(unsigned long));
247           skipped_break_addr = NULL;
248           *(unsigned long *) scp->sc_pc = displaced_after_inst;
249           os_flush_icache((os_vm_address_t) scp->sc_pc, sizeof(unsigned long));
250
251           scp->sc_mask = orig_sigmask;
252           break;
253
254       default:
255           interrupt_handle_now(signal, code, scp);
256           break;
257     }
258 }
259
260 static void
261 sigfpe_handler(int signal, int code, struct sigcontext *scp)
262 {
263     unsigned long bad_inst;
264     unsigned int op, rs, rt, rd, funct, dest;
265     int immed;
266     long result;
267
268     if (scp->sc_cause & CAUSE_BD)
269         bad_inst = *(unsigned long *) (scp->sc_pc + 4);
270     else
271         bad_inst = *(unsigned long *) (scp->sc_pc);
272
273     op = (bad_inst >> 26) & 0x3f;
274     rs = (bad_inst >> 21) & 0x1f;
275     rt = (bad_inst >> 16) & 0x1f;
276     rd = (bad_inst >> 11) & 0x1f;
277     funct = bad_inst & 0x3f;
278     immed = (((int) (bad_inst & 0xffff)) << 16) >> 16;
279
280     switch (op) {
281       case 0x0:         /* SPECIAL */
282           switch (funct) {
283             case 0x20:          /* ADD */
284                 /* Check to see if this is really a pa_interrupted hit */
285                 if (rs == reg_ALLOC && rt == reg_NL4) {
286                     scp->sc_regs[reg_ALLOC] +=
287                         (scp->sc_regs[reg_NL4] -
288                          PSEUDO_ATOMIC_INTERRUPTED_BIAS);
289                     arch_skip_instruction(scp);
290                     interrupt_handle_pending(scp);
291                     return;
292                 }
293                 result =
294                     fixnum_value(scp->sc_regs[rs]) +
295                     fixnum_value(scp->sc_regs[rt]);
296                 dest = rd;
297                 break;
298
299             case 0x22:          /* SUB */
300                 result =
301                     fixnum_value(scp->sc_regs[rs]) -
302                     fixnum_value(scp->sc_regs[rt]);
303                 dest = rd;
304                 break;
305
306             default:
307                 dest = 32;
308                 break;
309           }
310           break;
311
312       case 0x8:         /* ADDI */
313           result = fixnum_value(scp->sc_regs[rs]) + (immed >> 2);
314           dest = rt;
315           break;
316
317       default:
318           dest = 32;
319           break;
320     }
321
322     if (dest < 32) {
323         current_dynamic_space_free_pointer =
324             (lispobj *) scp->sc_regs[reg_ALLOC];
325
326         scp->sc_regs[dest] = alloc_number(result);
327
328         scp->sc_regs[reg_ALLOC] =
329             (unsigned long) current_dynamic_space_free_pointer;
330
331         arch_skip_instruction(scp);
332
333     } else
334         interrupt_handle_now(signal, code, scp);
335 }
336
337 void
338 arch_install_interrupt_handlers(void)
339 {
340     interrupt_install_low_level_handler(SIGTRAP, sigtrap_handler);
341     interrupt_install_low_level_handler(SIGFPE, sigfpe_handler);
342 }
343
344 extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
345
346 lispobj
347 funcall0(lispobj function)
348 {
349     lispobj *args = current_control_stack_pointer;
350
351     return call_into_lisp(function, args, 0);
352 }
353
354 lispobj
355 funcall1(lispobj function, lispobj arg0)
356 {
357     lispobj *args = current_control_stack_pointer;
358
359     current_control_stack_pointer += 1;
360     args[0] = arg0;
361
362     return call_into_lisp(function, args, 1);
363 }
364
365 lispobj
366 funcall2(lispobj function, lispobj arg0, lispobj arg1)
367 {
368     lispobj *args = current_control_stack_pointer;
369
370     current_control_stack_pointer += 2;
371     args[0] = arg0;
372     args[1] = arg1;
373
374     return call_into_lisp(function, args, 2);
375 }
376
377 lispobj
378 funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
379 {
380     lispobj *args = current_control_stack_pointer;
381
382     current_control_stack_pointer += 3;
383     args[0] = arg0;
384     args[1] = arg1;
385     args[2] = arg2;
386
387     return call_into_lisp(function, args, 3);
388 }
389
390
391 /* This is apparently called by emulate_branch, but isn't defined.  So */
392 /* just do nothing and hope it works... */
393
394 void
395 cacheflush(void)
396 {
397 }