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