]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/5e/arm.c
5e: push ERRMAX for note message on stack avoiding unaligned stack pointer
[plan9front.git] / sys / src / cmd / 5e / arm.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include <mach.h>
6 #include "dat.h"
7 #include "fns.h"
8
9 enum {
10         fI = 1<<25,
11         fP = 1<<24,
12         fLi = 1<<24,
13         fU = 1<<23,
14         fB = 1<<22,
15         fW = 1<<21,
16         fL = 1<<20,
17         fS = 1<<20,
18         fSg = 1<<6,
19         fH = 1<<5,
20 };
21
22 void
23 invalid(u32int instr)
24 {
25         suicide("undefined instruction %8ux @ %8ux", instr, P->R[15] - 4);
26 }
27
28 u32int
29 evenaddr(u32int addr, u32int mask)
30 {
31         if((addr & mask) == 0)
32                 return addr;
33         suicide("unaligned access %8ux @ %8ux", addr, P->R[15] - 4);
34         return addr & ~mask;
35 }
36
37 static u32int
38 doshift(u32int instr)
39 {
40         ulong amount, val;
41         
42         if((instr & (1<<4)) && (instr & (1<<7)))
43                 invalid(instr);
44         
45         if(instr & (1<<4))
46                 amount = P->R[(instr >> 8) & 15];
47         else
48                 amount = (instr >> 7) & 31;
49         val = P->R[instr & 15];
50         switch((instr >> 5) & 3) {
51         case 0:
52                 return val << amount;
53         case 1:
54                 return val >> amount;
55         case 2:
56                 return ((long) val) >> amount;
57         case 3:
58                 return (val >> amount) | (val << (32 - amount));
59         }
60         return 0;
61 }
62
63 static void
64 single(u32int instr)
65 {
66         long offset;
67         u32int addr;
68         u32int *Rn, *Rd;
69         void *targ;
70         Segment *seg;
71         
72         if(instr & fI) {
73                 if(instr & (1<<4))
74                         invalid(instr);
75                 offset = doshift(instr);
76         } else
77                 offset = instr & ((1<<12) - 1);
78         if(!(instr & fU))
79                 offset = - offset;
80         Rn = P->R + ((instr >> 16) & 15);
81         Rd = P->R + ((instr >> 12) & 15);
82         if((instr & (fW | fP)) == fW)
83                 invalid(instr);
84         if(Rn == P->R + 15) {
85                 if(instr & fW)
86                         invalid(instr);
87                 addr = P->R[15] + 4;
88         }
89         else
90                 addr = *Rn;
91         if(instr & fP)
92                 addr += offset;
93         if((instr & fB) == 0)
94                 addr = evenaddr(addr, 3);
95         targ = vaddr(addr, 4, &seg);
96         switch(instr & (fB | fL)) {
97         case 0:
98                 *(u32int*) targ = *Rd;
99                 break;
100         case fB:
101                 *(u8int*) targ = *Rd;
102                 break;
103         case fL:
104                 *Rd = *(u32int*) targ;
105                 break;
106         case fB | fL:
107                 *Rd = *(u8int*) targ;
108                 break;
109         }
110         if(Rd == P->R + 15 && !(instr & fL)) {
111                 if(instr & fB)
112                         *(u8int*) targ += 8;
113                 else
114                         *(u32int*) targ += 8;
115         }
116         segunlock(seg);
117         if(!(instr & fP))
118                 addr += offset;
119         if((instr & fW) || !(instr & fP))
120                 *Rn = addr;
121 }
122
123 static void
124 swap(u32int instr)
125 {
126         u32int *Rm, *Rn, *Rd, *targ, addr, tmp;
127         Segment *seg;
128         
129         Rm = P->R + (instr & 15);
130         Rd = P->R + ((instr >> 12) & 15);
131         Rn = P->R + ((instr >> 16) & 15);
132         if(Rm == P->R + 15 || Rd == P->R + 15 || Rn == P->R + 15)
133                 invalid(instr);
134         addr = *Rn;
135         if((instr & fB) == 0)
136                 addr = evenaddr(addr, 3);
137         targ = (u32int *) vaddr(addr, 4, &seg);
138         lock(&seg->lock);
139         if(instr & fB) {
140                 tmp = *(u8int*) targ;
141                 *(u8int*) targ = *Rm;
142                 *Rd = tmp;
143         } else {
144                 tmp = *targ;
145                 *targ = *Rm;
146                 *Rd = tmp;
147         }
148         unlock(&seg->lock);
149         segunlock(seg);
150 }
151
152 static u32int
153 add(u32int a, u32int b, u8int type, u8int *carry, u8int *overflow)
154 {
155         u32int res1;
156         u64int res2;
157
158         if(type) {
159                 res2 = (u64int)a - b + *carry - 1;
160                 res1 = res2;
161                 if(((a ^ b) & (1<<31)) && !((b ^ res1) & (1<<31))) *overflow = 1;
162                 if(res2 & 0x100000000LL) *carry = 0;
163                 else *carry = 1;        
164         } else {
165                 res2 = (u64int)a + b + *carry;
166                 res1 = res2;
167                 if(!((a ^ b) & (1<<31)) && ((b ^ res1) & (1<<31))) *overflow = 1;
168                 if(res2 & 0x100000000LL) *carry = 1;
169                 else *carry = 0;
170         }
171         return res1;
172 }
173
174 static void
175 alu(u32int instr)
176 {
177         u32int Rn, *Rd, operand, shift, result, op;
178         u8int carry, overflow;
179         
180         Rn = P->R[(instr >> 16) & 15];
181         Rd = P->R + ((instr >> 12) & 15);
182         if(((instr >> 16) & 15) == 15) {
183                 Rn += 4;
184                 if(!(instr & fI) && (instr & (1<<4)))
185                         Rn += 4;
186         }
187         if(Rd == P->R + 15 && (instr & fS))
188                 invalid(instr);
189         if(instr & fI) {
190                 operand = instr & 0xFF;
191                 shift = ((instr >> 8) & 15) << 1;
192                 operand = (operand >> shift) | (operand << (32 - shift));
193         } else
194                 operand = doshift(instr);
195         op = (instr >> 21) & 15;
196         carry = 0;
197         if(op >= 8 && op <= 11 && !(instr & fS))
198                 sysfatal("no PSR transfers plz");
199         if(op >= 5 && op < 8) {
200                 if(P->CPSR & flC)
201                         carry = 1;
202         } else {
203                 if(op != 4 && op != 5 && op != 11)
204                         carry = 1;
205         }
206         overflow = 0;
207         switch(op) {
208         case 0: case 8: result = Rn & operand; break;
209         case 1: case 9: result = Rn ^ operand; break;
210         case 2: case 6: case 10: result = add(Rn, operand, 1, &carry, &overflow); break;
211         case 3: case 7: result = add(operand, Rn, 1, &carry, &overflow); break;
212         case 4: case 5: case 11: result = add(operand, Rn, 0, &carry, &overflow); break;
213         case 12: result = Rn | operand; break;
214         case 13: result = operand; break;
215         case 14: result = Rn & ~operand; break;
216         case 15: result = ~operand; break;
217         default: result = 0; /* never happens */
218         }
219         if(instr & fS) {
220                 P->CPSR &= ~FLAGS;
221                 if(result == 0)
222                         P->CPSR |= flZ;
223                 if(result & (1<<31))
224                         P->CPSR |= flN;
225                 if(carry && op > 1 && op < 12)
226                         P->CPSR |= flC;
227                 if(overflow)
228                         P->CPSR |= flV;
229         }
230         if(op < 8 || op >= 12)
231                 *Rd = result;
232 }
233
234 static void
235 branch(u32int instr)
236 {
237         long offset;
238         
239         offset = instr & ((1<<24) - 1);
240         if(offset & (1<<23))
241                 offset |= ~((1 << 24) - 1);
242         offset *= 4;
243         if(instr & fLi)
244                 P->R[14] = P->R[15];
245         P->R[15] += offset + 4;
246 }
247
248 static void
249 halfword(u32int instr)
250 {
251         u32int offset, target, *Rn, *Rd;
252         Segment *seg;
253         
254         if(instr & (1<<22)) {
255                 offset = (instr & 15) | ((instr >> 4) & 0xF0);
256         } else {
257                 if((instr & 15) == 15)
258                         invalid(instr);
259                 offset = P->R[instr & 15];
260         }
261         if(!(instr & fU))
262                 offset = - offset;
263         if(!(instr & fP) && (instr & fW))
264                 invalid(instr);
265         Rn = P->R + ((instr >> 16) & 15);
266         Rd = P->R + ((instr >> 12) & 15);
267         if(Rn == P->R + 15 || Rd == P->R + 15)
268                 sysfatal("R15 in halfword");
269         target = *Rn;
270         if(instr & fP)
271                 target += offset;
272         if(instr & fH)
273                 target = evenaddr(target, 1);
274         switch(instr & (fSg | fH | fL)) {
275         case fSg: *(u8int*) vaddr(target, 1, &seg) = *Rd; break;
276         case fSg | fL: *Rd = (long) *(char*) vaddr(target, 1, &seg); break;
277         case fH: case fSg | fH: *(u16int*) vaddr(target, 2, &seg) = *Rd; break;
278         case fH | fL: *Rd = *(u16int*) vaddr(target, 2, &seg); break;
279         case fH | fL | fSg: *Rd = (long) *(short*) vaddr(target, 2, &seg); break;
280         }
281         segunlock(seg);
282         if(!(instr & fP))
283                 target += offset;
284         if(!(instr & fP) || (instr & fW))
285                 *Rn = target;
286 }
287
288 static void
289 block(u32int instr)
290 {
291         int i;
292         u32int targ, *Rn;
293         Segment *seg;
294
295         if(instr & (1<<22))
296                 invalid(instr);
297         Rn = P->R + ((instr >> 16) & 15);
298         if(Rn == P->R + 15 || instr & (1<<15))
299                 sysfatal("R15 block");
300         targ = evenaddr(*Rn, 3);
301         if(instr & fU) {
302                 for(i = 0; i < 16; i++) {
303                         if(!(instr & (1<<i)))
304                                 continue;
305                         if(instr & fP)
306                                 targ += 4;
307                         if(instr & fL)
308                                 P->R[i] = *(u32int*) vaddr(targ, 4, &seg);
309                         else
310                                 *(u32int*) vaddr(targ, 4, &seg) = P->R[i];
311                         segunlock(seg);
312                         if(!(instr & fP))
313                                 targ += 4;
314                 }
315         } else {
316                 for(i = 15; i >= 0; i--) {
317                         if(!(instr & (1<<i)))
318                                 continue;
319                         if(instr & fP)
320                                 targ -= 4;
321                         if(instr & fL)
322                                 P->R[i] = *(u32int*) vaddr(targ, 4, &seg);
323                         else
324                                 *(u32int*) vaddr(targ, 4, &seg) = P->R[i];
325                         segunlock(seg);
326                         if(!(instr & fP))
327                                 targ -= 4;
328                 }
329         }
330         if(instr & fW)
331                 *Rn = targ;
332 }
333
334 static void
335 multiply(u32int instr)
336 {
337         u32int *Rd, *Rn, *Rs, *Rm, res;
338         
339         Rm = P->R + (instr & 15);
340         Rs = P->R + ((instr >> 8) & 15);
341         Rn = P->R + ((instr >> 12) & 15);
342         Rd = P->R + ((instr >> 16) & 15);
343         if(Rd == Rm || Rm == P->R + 15 || Rs == P->R + 15 || Rn == P->R + 15 || Rd == P->R + 15)
344                 invalid(instr);
345         res = *Rm * *Rs;
346         if(instr & (1<<21))
347                 res += *Rn;
348         *Rd = res;
349         if(instr & (1<<20)) {
350                 P->CPSR &= ~(flN | flZ);
351                 if(res & (1<<31))
352                         P->CPSR |= flN;
353                 if(res == 0)
354                         P->CPSR |= flZ;
355         }
356 }
357
358 static void
359 multiplylong(u32int instr)
360 {
361         u32int *RdH, *RdL, *Rs, *Rm;
362         u64int res;
363         
364         Rm = P->R + (instr & 15);
365         Rs = P->R + ((instr >> 8) & 15);
366         RdL = P->R + ((instr >> 12) & 15);
367         RdH = P->R + ((instr >> 16) & 15);
368         if(RdL == RdH || RdH == Rm || RdL == Rm || Rm == P->R + 15 || Rs == P->R + 15 || RdL == P->R + 15 || RdH == P->R + 15)
369                 invalid(instr);
370         if(instr & (1<<22)) {
371                 res = *Rs;
372                 res *= *Rm;
373         } else
374                 res = ((vlong)*(int*)Rs) * *(int*)Rm;
375         if(instr & (1<<21)) {
376                 res += *RdL;
377                 res += ((uvlong)*RdH) << 32;
378         }
379         *RdL = res;
380         *RdH = res >> 32;
381         if(instr & (1<<20)) {
382                 P->CPSR &= ~FLAGS;
383                 if(res == 0)
384                         P->CPSR |= flN;
385                 if(res & (1LL<<63))
386                         P->CPSR |= flV;
387         }
388 }
389
390 static void
391 singleex(u32int instr)
392 {
393         u32int *Rn, *Rd, *Rm, *targ, addr;
394         Segment *seg;
395         
396         Rd = P->R + ((instr >> 12) & 15);
397         Rn = P->R + ((instr >> 16) & 15);
398         if(Rd == P->R + 15 || Rn == P->R + 15)
399                 invalid(instr);
400         addr = evenaddr(*Rn, 3);
401         if(instr & fS) {
402                 targ = vaddr(addr, 4, &seg);
403                 lock(&seg->lock);
404                 *Rd = *targ;
405                 segunlock(seg);
406         } else {
407                 Rm = P->R + (instr & 15);
408                 if(Rm == P->R + 15)
409                         invalid(instr);
410                 targ = vaddr(addr, 4, &seg);
411                 if(canlock(&seg->lock)) {
412                         *Rd = 1;
413                 } else {
414                         *targ = *Rm;
415                         unlock(&seg->lock);
416                         *Rd = 0;
417                 }
418                 segunlock(seg);
419         }
420 }
421
422 void
423 step(void)
424 {
425         u32int instr;
426         Segment *seg;
427
428         instr = *(u32int*) vaddr(P->R[15], 4, &seg);
429         segunlock(seg);
430         if(fulltrace) {
431                 print("%d ", P->pid);
432                 if(havesymbols) {
433                         Symbol s;
434                         char buf[512];
435                         
436                         if(findsym(P->R[15], CTEXT, &s) >= 0)
437                                 print("%s ", s.name);
438                         if(fileline(buf, 512, P->R[15]) >= 0)
439                                 print("%s ", buf);
440                 }
441                 print("%.8ux %.8ux %c%c%c%c\n", P->R[15], instr,
442                         (P->CPSR & flZ) ? 'Z' : ' ',
443                         (P->CPSR & flC) ? 'C' : ' ',
444                         (P->CPSR & flN) ? 'N' : ' ',
445                         (P->CPSR & flV) ? 'V' : ' '
446                         );
447         }
448         P->R[15] += 4;
449         switch(instr >> 28) {
450         case 0x0: if(!(P->CPSR & flZ)) return; break;
451         case 0x1: if(P->CPSR & flZ) return; break;
452         case 0x2: if(!(P->CPSR & flC)) return; break;
453         case 0x3: if(P->CPSR & flC) return; break;
454         case 0x4: if(!(P->CPSR & flN)) return; break;
455         case 0x5: if(P->CPSR & flN) return; break;
456         case 0x6: if(!(P->CPSR & flV)) return; break;
457         case 0x7: if(P->CPSR & flV) return; break;
458         case 0x8: if(!(P->CPSR & flC) || (P->CPSR & flZ)) return; break;
459         case 0x9: if((P->CPSR & flC) && !(P->CPSR & flZ)) return; break;
460         case 0xA: if(!(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
461         case 0xB: if(!(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
462         case 0xC: if((P->CPSR & flZ) || !(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
463         case 0xD: if(!(P->CPSR & flZ) && !(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
464         case 0xE: break;
465         default: sysfatal("condition code %x not implemented", instr >> 28);
466         }
467         if((instr & 0x0FB00FF0) == 0x01000090)
468                 swap(instr);
469         else if((instr & 0x0FE000F0) == 0x01800090)
470                 singleex(instr);
471         else if((instr & 0x0FC000F0) == 0x90)
472                 multiply(instr);
473         else if((instr & 0x0F8000F0) == 0x800090)
474                 multiplylong(instr);
475         else if((instr & ((1<<26) | (1<<27))) == (1 << 26))
476                 single(instr);
477         else if((instr & 0x0E000090) == 0x90 && (instr & 0x60))
478                 halfword(instr);
479         else if((instr & ((1<<26) | (1<<27))) == 0)
480                 alu(instr);
481         else if((instr & (7<<25)) == (5 << 25))
482                 branch(instr);
483         else if((instr & (15<<24)) == (15 << 24))
484                 syscall();
485         else if((instr & (7<<25)) == (4 << 25))
486                 block(instr);
487         else if((instr & 0x0E000F00) == 0x0C000100)
488                 fpatransfer(instr);
489         else if((instr & 0x0E000F10) == 0x0E000100)
490                 fpaoperation(instr);
491         else if((instr & 0x0E000F10) == 0x0E000110)
492                 fparegtransfer(instr);
493         else if(vfp && ((instr & 0x0F000A10) == 0x0E000A00))
494                 vfpoperation(instr);
495         else if(vfp && ((instr & 0x0F000F10) == 0x0E000A10))
496                 vfpregtransfer(instr);
497         else if(vfp && ((instr & 0x0F000A00) == 0x0D000A00))
498                 vfprmtransfer(instr);
499         else
500                 invalid(instr);
501 }