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