drc: optional address error exception support
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / assem_arm.c
... / ...
CommitLineData
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus/PCSX - assem_arm.c *
3 * Copyright (C) 2009-2011 Ari64 *
4 * Copyright (C) 2010-2021 GraÅžvydas "notaz" Ignotas *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program 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 *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22#define FLAGLESS
23#include "../gte.h"
24#undef FLAGLESS
25#include "../gte_arm.h"
26#include "../gte_neon.h"
27#include "pcnt.h"
28#include "arm_features.h"
29
30#ifdef DRC_DBG
31#pragma GCC diagnostic ignored "-Wunused-function"
32#pragma GCC diagnostic ignored "-Wunused-variable"
33#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
34#endif
35
36void indirect_jump_indexed();
37void indirect_jump();
38void do_interrupt();
39void jump_vaddr_r0();
40void jump_vaddr_r1();
41void jump_vaddr_r2();
42void jump_vaddr_r3();
43void jump_vaddr_r4();
44void jump_vaddr_r5();
45void jump_vaddr_r6();
46void jump_vaddr_r7();
47void jump_vaddr_r8();
48void jump_vaddr_r9();
49void jump_vaddr_r10();
50void jump_vaddr_r12();
51
52void * const jump_vaddr_reg[16] = {
53 jump_vaddr_r0,
54 jump_vaddr_r1,
55 jump_vaddr_r2,
56 jump_vaddr_r3,
57 jump_vaddr_r4,
58 jump_vaddr_r5,
59 jump_vaddr_r6,
60 jump_vaddr_r7,
61 jump_vaddr_r8,
62 jump_vaddr_r9,
63 jump_vaddr_r10,
64 0,
65 jump_vaddr_r12,
66 0,
67 0,
68 0
69};
70
71void invalidate_addr_r0();
72void invalidate_addr_r1();
73void invalidate_addr_r2();
74void invalidate_addr_r3();
75void invalidate_addr_r4();
76void invalidate_addr_r5();
77void invalidate_addr_r6();
78void invalidate_addr_r7();
79void invalidate_addr_r8();
80void invalidate_addr_r9();
81void invalidate_addr_r10();
82void invalidate_addr_r12();
83
84const u_int invalidate_addr_reg[16] = {
85 (int)invalidate_addr_r0,
86 (int)invalidate_addr_r1,
87 (int)invalidate_addr_r2,
88 (int)invalidate_addr_r3,
89 (int)invalidate_addr_r4,
90 (int)invalidate_addr_r5,
91 (int)invalidate_addr_r6,
92 (int)invalidate_addr_r7,
93 (int)invalidate_addr_r8,
94 (int)invalidate_addr_r9,
95 (int)invalidate_addr_r10,
96 0,
97 (int)invalidate_addr_r12,
98 0,
99 0,
100 0};
101
102/* Linker */
103
104static void set_jump_target(void *addr, void *target_)
105{
106 u_int target = (u_int)target_;
107 u_char *ptr = addr;
108 u_int *ptr2=(u_int *)ptr;
109 if(ptr[3]==0xe2) {
110 assert((target-(u_int)ptr2-8)<1024);
111 assert(((uintptr_t)addr&3)==0);
112 assert((target&3)==0);
113 *ptr2=(*ptr2&0xFFFFF000)|((target-(u_int)ptr2-8)>>2)|0xF00;
114 //printf("target=%x addr=%p insn=%x\n",target,addr,*ptr2);
115 }
116 else if(ptr[3]==0x72) {
117 // generated by emit_jno_unlikely
118 if((target-(u_int)ptr2-8)<1024) {
119 assert(((uintptr_t)addr&3)==0);
120 assert((target&3)==0);
121 *ptr2=(*ptr2&0xFFFFF000)|((target-(u_int)ptr2-8)>>2)|0xF00;
122 }
123 else if((target-(u_int)ptr2-8)<4096&&!((target-(u_int)ptr2-8)&15)) {
124 assert(((uintptr_t)addr&3)==0);
125 assert((target&3)==0);
126 *ptr2=(*ptr2&0xFFFFF000)|((target-(u_int)ptr2-8)>>4)|0xE00;
127 }
128 else *ptr2=(0x7A000000)|(((target-(u_int)ptr2-8)<<6)>>8);
129 }
130 else {
131 assert((ptr[3]&0x0e)==0xa);
132 *ptr2=(*ptr2&0xFF000000)|(((target-(u_int)ptr2-8)<<6)>>8);
133 }
134}
135
136// This optionally copies the instruction from the target of the branch into
137// the space before the branch. Works, but the difference in speed is
138// usually insignificant.
139#if 0
140static void set_jump_target_fillslot(int addr,u_int target,int copy)
141{
142 u_char *ptr=(u_char *)addr;
143 u_int *ptr2=(u_int *)ptr;
144 assert(!copy||ptr2[-1]==0xe28dd000);
145 if(ptr[3]==0xe2) {
146 assert(!copy);
147 assert((target-(u_int)ptr2-8)<4096);
148 *ptr2=(*ptr2&0xFFFFF000)|(target-(u_int)ptr2-8);
149 }
150 else {
151 assert((ptr[3]&0x0e)==0xa);
152 u_int target_insn=*(u_int *)target;
153 if((target_insn&0x0e100000)==0) { // ALU, no immediate, no flags
154 copy=0;
155 }
156 if((target_insn&0x0c100000)==0x04100000) { // Load
157 copy=0;
158 }
159 if(target_insn&0x08000000) {
160 copy=0;
161 }
162 if(copy) {
163 ptr2[-1]=target_insn;
164 target+=4;
165 }
166 *ptr2=(*ptr2&0xFF000000)|(((target-(u_int)ptr2-8)<<6)>>8);
167 }
168}
169#endif
170
171/* Literal pool */
172static void add_literal(int addr,int val)
173{
174 assert(literalcount<sizeof(literals)/sizeof(literals[0]));
175 literals[literalcount][0]=addr;
176 literals[literalcount][1]=val;
177 literalcount++;
178}
179
180// from a pointer to external jump stub (which was produced by emit_extjump2)
181// find where the jumping insn is
182static void *find_extjump_insn(void *stub)
183{
184 int *ptr=(int *)(stub+4);
185 assert((*ptr&0x0fff0000)==0x059f0000); // ldr rx, [pc, #ofs]
186 u_int offset=*ptr&0xfff;
187 void **l_ptr=(void *)ptr+offset+8;
188 return *l_ptr;
189}
190
191// find where external branch is liked to using addr of it's stub:
192// get address that insn one after stub loads (dyna_linker arg1),
193// treat it as a pointer to branch insn,
194// return addr where that branch jumps to
195#if 0
196static void *get_pointer(void *stub)
197{
198 //printf("get_pointer(%x)\n",(int)stub);
199 int *i_ptr=find_extjump_insn(stub);
200 assert((*i_ptr&0x0f000000)==0x0a000000); // b
201 return (u_char *)i_ptr+((*i_ptr<<8)>>6)+8;
202}
203#endif
204
205// Allocate a specific ARM register.
206static void alloc_arm_reg(struct regstat *cur,int i,signed char reg,int hr)
207{
208 int n;
209 int dirty=0;
210
211 // see if it's already allocated (and dealloc it)
212 for(n=0;n<HOST_REGS;n++)
213 {
214 if(n!=EXCLUDE_REG&&cur->regmap[n]==reg) {
215 dirty=(cur->dirty>>n)&1;
216 cur->regmap[n]=-1;
217 }
218 }
219
220 cur->regmap[hr]=reg;
221 cur->dirty&=~(1<<hr);
222 cur->dirty|=dirty<<hr;
223 cur->isconst&=~(1<<hr);
224}
225
226// Alloc cycle count into dedicated register
227static void alloc_cc(struct regstat *cur,int i)
228{
229 alloc_arm_reg(cur,i,CCREG,HOST_CCREG);
230}
231
232/* Assembler */
233
234static unused char regname[16][4] = {
235 "r0",
236 "r1",
237 "r2",
238 "r3",
239 "r4",
240 "r5",
241 "r6",
242 "r7",
243 "r8",
244 "r9",
245 "r10",
246 "fp",
247 "r12",
248 "sp",
249 "lr",
250 "pc"};
251
252static void output_w32(u_int word)
253{
254 *((u_int *)out)=word;
255 out+=4;
256}
257
258static u_int rd_rn_rm(u_int rd, u_int rn, u_int rm)
259{
260 assert(rd<16);
261 assert(rn<16);
262 assert(rm<16);
263 return((rn<<16)|(rd<<12)|rm);
264}
265
266static u_int rd_rn_imm_shift(u_int rd, u_int rn, u_int imm, u_int shift)
267{
268 assert(rd<16);
269 assert(rn<16);
270 assert(imm<256);
271 assert((shift&1)==0);
272 return((rn<<16)|(rd<<12)|(((32-shift)&30)<<7)|imm);
273}
274
275static u_int genimm(u_int imm,u_int *encoded)
276{
277 *encoded=0;
278 if(imm==0) return 1;
279 int i=32;
280 while(i>0)
281 {
282 if(imm<256) {
283 *encoded=((i&30)<<7)|imm;
284 return 1;
285 }
286 imm=(imm>>2)|(imm<<30);i-=2;
287 }
288 return 0;
289}
290
291static void genimm_checked(u_int imm,u_int *encoded)
292{
293 u_int ret=genimm(imm,encoded);
294 assert(ret);
295 (void)ret;
296}
297
298static u_int genjmp(u_int addr)
299{
300 if (addr < 3) return 0; // a branch that will be patched later
301 int offset = addr-(int)out-8;
302 if (offset < -33554432 || offset >= 33554432) {
303 SysPrintf("genjmp: out of range: %08x\n", offset);
304 abort();
305 return 0;
306 }
307 return ((u_int)offset>>2)&0xffffff;
308}
309
310static unused void emit_breakpoint(void)
311{
312 assem_debug("bkpt #0\n");
313 //output_w32(0xe1200070);
314 output_w32(0xe7f001f0);
315}
316
317static void emit_mov(int rs,int rt)
318{
319 assem_debug("mov %s,%s\n",regname[rt],regname[rs]);
320 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs));
321}
322
323static void emit_movs(int rs,int rt)
324{
325 assem_debug("movs %s,%s\n",regname[rt],regname[rs]);
326 output_w32(0xe1b00000|rd_rn_rm(rt,0,rs));
327}
328
329static void emit_add(int rs1,int rs2,int rt)
330{
331 assem_debug("add %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
332 output_w32(0xe0800000|rd_rn_rm(rt,rs1,rs2));
333}
334
335static void emit_adds(int rs1,int rs2,int rt)
336{
337 assem_debug("adds %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
338 output_w32(0xe0900000|rd_rn_rm(rt,rs1,rs2));
339}
340#define emit_adds_ptr emit_adds
341
342static void emit_adcs(int rs1,int rs2,int rt)
343{
344 assem_debug("adcs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
345 output_w32(0xe0b00000|rd_rn_rm(rt,rs1,rs2));
346}
347
348static void emit_neg(int rs, int rt)
349{
350 assem_debug("rsb %s,%s,#0\n",regname[rt],regname[rs]);
351 output_w32(0xe2600000|rd_rn_rm(rt,rs,0));
352}
353
354static void emit_negs(int rs, int rt)
355{
356 assem_debug("rsbs %s,%s,#0\n",regname[rt],regname[rs]);
357 output_w32(0xe2700000|rd_rn_rm(rt,rs,0));
358}
359
360static void emit_sub(int rs1,int rs2,int rt)
361{
362 assem_debug("sub %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
363 output_w32(0xe0400000|rd_rn_rm(rt,rs1,rs2));
364}
365
366static void emit_subs(int rs1,int rs2,int rt)
367{
368 assem_debug("subs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
369 output_w32(0xe0500000|rd_rn_rm(rt,rs1,rs2));
370}
371
372static void emit_zeroreg(int rt)
373{
374 assem_debug("mov %s,#0\n",regname[rt]);
375 output_w32(0xe3a00000|rd_rn_rm(rt,0,0));
376}
377
378static void emit_loadlp(u_int imm,u_int rt)
379{
380 add_literal((int)out,imm);
381 assem_debug("ldr %s,pc+? [=%x]\n",regname[rt],imm);
382 output_w32(0xe5900000|rd_rn_rm(rt,15,0));
383}
384
385#ifdef HAVE_ARMV7
386static void emit_movw(u_int imm,u_int rt)
387{
388 assert(imm<65536);
389 assem_debug("movw %s,#%d (0x%x)\n",regname[rt],imm,imm);
390 output_w32(0xe3000000|rd_rn_rm(rt,0,0)|(imm&0xfff)|((imm<<4)&0xf0000));
391}
392
393static void emit_movt(u_int imm,u_int rt)
394{
395 assem_debug("movt %s,#%d (0x%x)\n",regname[rt],imm&0xffff0000,imm&0xffff0000);
396 output_w32(0xe3400000|rd_rn_rm(rt,0,0)|((imm>>16)&0xfff)|((imm>>12)&0xf0000));
397}
398#endif
399
400static void emit_movimm(u_int imm,u_int rt)
401{
402 u_int armval;
403 if(genimm(imm,&armval)) {
404 assem_debug("mov %s,#%d\n",regname[rt],imm);
405 output_w32(0xe3a00000|rd_rn_rm(rt,0,0)|armval);
406 }else if(genimm(~imm,&armval)) {
407 assem_debug("mvn %s,#%d\n",regname[rt],imm);
408 output_w32(0xe3e00000|rd_rn_rm(rt,0,0)|armval);
409 }else if(imm<65536) {
410 #ifndef HAVE_ARMV7
411 assem_debug("mov %s,#%d\n",regname[rt],imm&0xFF00);
412 output_w32(0xe3a00000|rd_rn_imm_shift(rt,0,imm>>8,8));
413 assem_debug("add %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF);
414 output_w32(0xe2800000|rd_rn_imm_shift(rt,rt,imm&0xff,0));
415 #else
416 emit_movw(imm,rt);
417 #endif
418 }else{
419 #ifndef HAVE_ARMV7
420 emit_loadlp(imm,rt);
421 #else
422 emit_movw(imm&0x0000FFFF,rt);
423 emit_movt(imm&0xFFFF0000,rt);
424 #endif
425 }
426}
427
428static void emit_pcreladdr(u_int rt)
429{
430 assem_debug("add %s,pc,#?\n",regname[rt]);
431 output_w32(0xe2800000|rd_rn_rm(rt,15,0));
432}
433
434static void emit_loadreg(int r, int hr)
435{
436 assert(hr != EXCLUDE_REG);
437 if (r == 0)
438 emit_zeroreg(hr);
439 else {
440 void *addr;
441 switch (r) {
442 //case HIREG: addr = &hi; break;
443 //case LOREG: addr = &lo; break;
444 case CCREG: addr = &cycle_count; break;
445 case CSREG: addr = &psxRegs.CP0.n.SR; break;
446 case INVCP: addr = &invc_ptr; break;
447 case ROREG: addr = &ram_offset; break;
448 default:
449 assert(r < 34);
450 addr = &psxRegs.GPR.r[r];
451 break;
452 }
453 u_int offset = (u_char *)addr - (u_char *)&dynarec_local;
454 assert(offset<4096);
455 assem_debug("ldr %s,fp+%d # r%d\n",regname[hr],offset,r);
456 output_w32(0xe5900000|rd_rn_rm(hr,FP,0)|offset);
457 }
458}
459
460static void emit_storereg(int r, int hr)
461{
462 assert(hr != EXCLUDE_REG);
463 int addr = (int)&psxRegs.GPR.r[r];
464 switch (r) {
465 //case HIREG: addr = &hi; break;
466 //case LOREG: addr = &lo; break;
467 case CCREG: addr = (int)&cycle_count; break;
468 default: assert(r < 34); break;
469 }
470 u_int offset = addr-(u_int)&dynarec_local;
471 assert(offset<4096);
472 assem_debug("str %s,fp+%d # r%d\n",regname[hr],offset,r);
473 output_w32(0xe5800000|rd_rn_rm(hr,FP,0)|offset);
474}
475
476static void emit_test(int rs, int rt)
477{
478 assem_debug("tst %s,%s\n",regname[rs],regname[rt]);
479 output_w32(0xe1100000|rd_rn_rm(0,rs,rt));
480}
481
482static void emit_testimm(int rs,int imm)
483{
484 u_int armval;
485 assem_debug("tst %s,#%d\n",regname[rs],imm);
486 genimm_checked(imm,&armval);
487 output_w32(0xe3100000|rd_rn_rm(0,rs,0)|armval);
488}
489
490static void emit_testeqimm(int rs,int imm)
491{
492 u_int armval;
493 assem_debug("tsteq %s,$%d\n",regname[rs],imm);
494 genimm_checked(imm,&armval);
495 output_w32(0x03100000|rd_rn_rm(0,rs,0)|armval);
496}
497
498static void emit_not(int rs,int rt)
499{
500 assem_debug("mvn %s,%s\n",regname[rt],regname[rs]);
501 output_w32(0xe1e00000|rd_rn_rm(rt,0,rs));
502}
503
504static void emit_mvneq(int rs,int rt)
505{
506 assem_debug("mvneq %s,%s\n",regname[rt],regname[rs]);
507 output_w32(0x01e00000|rd_rn_rm(rt,0,rs));
508}
509
510static void emit_and(u_int rs1,u_int rs2,u_int rt)
511{
512 assem_debug("and %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
513 output_w32(0xe0000000|rd_rn_rm(rt,rs1,rs2));
514}
515
516static void emit_or(u_int rs1,u_int rs2,u_int rt)
517{
518 assem_debug("orr %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
519 output_w32(0xe1800000|rd_rn_rm(rt,rs1,rs2));
520}
521
522static void emit_orrshl_imm(u_int rs,u_int imm,u_int rt)
523{
524 assert(rs<16);
525 assert(rt<16);
526 assert(imm<32);
527 assem_debug("orr %s,%s,%s,lsl #%d\n",regname[rt],regname[rt],regname[rs],imm);
528 output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|(imm<<7));
529}
530
531static void emit_orrshr_imm(u_int rs,u_int imm,u_int rt)
532{
533 assert(rs<16);
534 assert(rt<16);
535 assert(imm<32);
536 assem_debug("orr %s,%s,%s,lsr #%d\n",regname[rt],regname[rt],regname[rs],imm);
537 output_w32(0xe1800020|rd_rn_rm(rt,rt,rs)|(imm<<7));
538}
539
540static void emit_xor(u_int rs1,u_int rs2,u_int rt)
541{
542 assem_debug("eor %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
543 output_w32(0xe0200000|rd_rn_rm(rt,rs1,rs2));
544}
545
546static void emit_xorsar_imm(u_int rs1,u_int rs2,u_int imm,u_int rt)
547{
548 assem_debug("eor %s,%s,%s,asr #%d\n",regname[rt],regname[rs1],regname[rs2],imm);
549 output_w32(0xe0200040|rd_rn_rm(rt,rs1,rs2)|(imm<<7));
550}
551
552static void emit_addimm(u_int rs,int imm,u_int rt)
553{
554 assert(rs<16);
555 assert(rt<16);
556 if(imm!=0) {
557 u_int armval;
558 if(genimm(imm,&armval)) {
559 assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm);
560 output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
561 }else if(genimm(-imm,&armval)) {
562 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-imm);
563 output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
564 #ifdef HAVE_ARMV7
565 }else if(rt!=rs&&(u_int)imm<65536) {
566 emit_movw(imm&0x0000ffff,rt);
567 emit_add(rs,rt,rt);
568 }else if(rt!=rs&&(u_int)-imm<65536) {
569 emit_movw(-imm&0x0000ffff,rt);
570 emit_sub(rs,rt,rt);
571 #endif
572 }else if((u_int)-imm<65536) {
573 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],(-imm)&0xFF00);
574 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF);
575 output_w32(0xe2400000|rd_rn_imm_shift(rt,rs,(-imm)>>8,8));
576 output_w32(0xe2400000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0));
577 }else {
578 do {
579 int shift = (ffs(imm) - 1) & ~1;
580 int imm8 = imm & (0xff << shift);
581 genimm_checked(imm8,&armval);
582 assem_debug("add %s,%s,#0x%x\n",regname[rt],regname[rs],imm8);
583 output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
584 rs = rt;
585 imm &= ~imm8;
586 }
587 while (imm != 0);
588 }
589 }
590 else if(rs!=rt) emit_mov(rs,rt);
591}
592
593static void emit_addimm_ptr(u_int rs, uintptr_t imm, u_int rt)
594{
595 emit_addimm(rs, imm, rt);
596}
597
598static void emit_addimm_and_set_flags3(u_int rs, int imm, u_int rt)
599{
600 assert(imm>-65536&&imm<65536);
601 u_int armval;
602 if (genimm(imm, &armval)) {
603 assem_debug("adds %s,%s,#%d\n",regname[rt],regname[rs],imm);
604 output_w32(0xe2900000|rd_rn_rm(rt,rs,0)|armval);
605 } else if (genimm(-imm, &armval)) {
606 assem_debug("subs %s,%s,#%d\n",regname[rt],regname[rs],imm);
607 output_w32(0xe2500000|rd_rn_rm(rt,rs,0)|armval);
608 } else if (rs != rt) {
609 emit_movimm(imm, rt);
610 emit_adds(rs, rt, rt);
611 } else if (imm < 0) {
612 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],(-imm)&0xFF00);
613 assem_debug("subs %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF);
614 output_w32(0xe2400000|rd_rn_imm_shift(rt,rs,(-imm)>>8,8));
615 output_w32(0xe2500000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0));
616 } else {
617 assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00);
618 assem_debug("adds %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF);
619 output_w32(0xe2800000|rd_rn_imm_shift(rt,rs,imm>>8,8));
620 output_w32(0xe2900000|rd_rn_imm_shift(rt,rt,imm&0xff,0));
621 }
622}
623
624static void emit_addimm_and_set_flags(int imm, u_int rt)
625{
626 emit_addimm_and_set_flags3(rt, imm, rt);
627}
628
629static void emit_addnop(u_int r)
630{
631 assert(r<16);
632 assem_debug("add %s,%s,#0 (nop)\n",regname[r],regname[r]);
633 output_w32(0xe2800000|rd_rn_rm(r,r,0));
634}
635
636static void emit_andimm(int rs,int imm,int rt)
637{
638 u_int armval;
639 if(imm==0) {
640 emit_zeroreg(rt);
641 }else if(genimm(imm,&armval)) {
642 assem_debug("and %s,%s,#%d\n",regname[rt],regname[rs],imm);
643 output_w32(0xe2000000|rd_rn_rm(rt,rs,0)|armval);
644 }else if(genimm(~imm,&armval)) {
645 assem_debug("bic %s,%s,#%d\n",regname[rt],regname[rs],imm);
646 output_w32(0xe3c00000|rd_rn_rm(rt,rs,0)|armval);
647 }else if(imm==65535) {
648 #ifndef HAVE_ARMV6
649 assem_debug("bic %s,%s,#FF000000\n",regname[rt],regname[rs]);
650 output_w32(0xe3c00000|rd_rn_rm(rt,rs,0)|0x4FF);
651 assem_debug("bic %s,%s,#00FF0000\n",regname[rt],regname[rt]);
652 output_w32(0xe3c00000|rd_rn_rm(rt,rt,0)|0x8FF);
653 #else
654 assem_debug("uxth %s,%s\n",regname[rt],regname[rs]);
655 output_w32(0xe6ff0070|rd_rn_rm(rt,0,rs));
656 #endif
657 }else{
658 assert(imm>0&&imm<65535);
659 #ifndef HAVE_ARMV7
660 assem_debug("mov r14,#%d\n",imm&0xFF00);
661 output_w32(0xe3a00000|rd_rn_imm_shift(HOST_TEMPREG,0,imm>>8,8));
662 assem_debug("add r14,r14,#%d\n",imm&0xFF);
663 output_w32(0xe2800000|rd_rn_imm_shift(HOST_TEMPREG,HOST_TEMPREG,imm&0xff,0));
664 #else
665 emit_movw(imm,HOST_TEMPREG);
666 #endif
667 assem_debug("and %s,%s,r14\n",regname[rt],regname[rs]);
668 output_w32(0xe0000000|rd_rn_rm(rt,rs,HOST_TEMPREG));
669 }
670}
671
672static void emit_orimm(int rs,int imm,int rt)
673{
674 u_int armval;
675 if(imm==0) {
676 if(rs!=rt) emit_mov(rs,rt);
677 }else if(genimm(imm,&armval)) {
678 assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm);
679 output_w32(0xe3800000|rd_rn_rm(rt,rs,0)|armval);
680 }else{
681 assert(imm>0&&imm<65536);
682 assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00);
683 assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF);
684 output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8));
685 output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0));
686 }
687}
688
689static void emit_xorimm(int rs,int imm,int rt)
690{
691 u_int armval;
692 if(imm==0) {
693 if(rs!=rt) emit_mov(rs,rt);
694 }else if(genimm(imm,&armval)) {
695 assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm);
696 output_w32(0xe2200000|rd_rn_rm(rt,rs,0)|armval);
697 }else{
698 assert(imm>0&&imm<65536);
699 assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00);
700 assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF);
701 output_w32(0xe2200000|rd_rn_imm_shift(rt,rs,imm>>8,8));
702 output_w32(0xe2200000|rd_rn_imm_shift(rt,rt,imm&0xff,0));
703 }
704}
705
706static void emit_shlimm(int rs,u_int imm,int rt)
707{
708 assert(imm>0);
709 assert(imm<32);
710 //if(imm==1) ...
711 assem_debug("lsl %s,%s,#%d\n",regname[rt],regname[rs],imm);
712 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|(imm<<7));
713}
714
715static void emit_lsls_imm(int rs,int imm,int rt)
716{
717 assert(imm>0);
718 assert(imm<32);
719 assem_debug("lsls %s,%s,#%d\n",regname[rt],regname[rs],imm);
720 output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)|(imm<<7));
721}
722
723static unused void emit_lslpls_imm(int rs,int imm,int rt)
724{
725 assert(imm>0);
726 assert(imm<32);
727 assem_debug("lslpls %s,%s,#%d\n",regname[rt],regname[rs],imm);
728 output_w32(0x51b00000|rd_rn_rm(rt,0,rs)|(imm<<7));
729}
730
731static void emit_shrimm(int rs,u_int imm,int rt)
732{
733 assert(imm>0);
734 assert(imm<32);
735 assem_debug("lsr %s,%s,#%d\n",regname[rt],regname[rs],imm);
736 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7));
737}
738
739static void emit_sarimm(int rs,u_int imm,int rt)
740{
741 assert(imm>0);
742 assert(imm<32);
743 assem_debug("asr %s,%s,#%d\n",regname[rt],regname[rs],imm);
744 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x40|(imm<<7));
745}
746
747static void emit_rorimm(int rs,u_int imm,int rt)
748{
749 assert(imm>0);
750 assert(imm<32);
751 assem_debug("ror %s,%s,#%d\n",regname[rt],regname[rs],imm);
752 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x60|(imm<<7));
753}
754
755static void emit_signextend16(int rs,int rt)
756{
757 #ifndef HAVE_ARMV6
758 emit_shlimm(rs,16,rt);
759 emit_sarimm(rt,16,rt);
760 #else
761 assem_debug("sxth %s,%s\n",regname[rt],regname[rs]);
762 output_w32(0xe6bf0070|rd_rn_rm(rt,0,rs));
763 #endif
764}
765
766static void emit_signextend8(int rs,int rt)
767{
768 #ifndef HAVE_ARMV6
769 emit_shlimm(rs,24,rt);
770 emit_sarimm(rt,24,rt);
771 #else
772 assem_debug("sxtb %s,%s\n",regname[rt],regname[rs]);
773 output_w32(0xe6af0070|rd_rn_rm(rt,0,rs));
774 #endif
775}
776
777static void emit_shl(u_int rs,u_int shift,u_int rt)
778{
779 assert(rs<16);
780 assert(rt<16);
781 assert(shift<16);
782 //if(imm==1) ...
783 assem_debug("lsl %s,%s,%s\n",regname[rt],regname[rs],regname[shift]);
784 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x10|(shift<<8));
785}
786
787static void emit_shr(u_int rs,u_int shift,u_int rt)
788{
789 assert(rs<16);
790 assert(rt<16);
791 assert(shift<16);
792 assem_debug("lsr %s,%s,%s\n",regname[rt],regname[rs],regname[shift]);
793 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x30|(shift<<8));
794}
795
796static void emit_sar(u_int rs,u_int shift,u_int rt)
797{
798 assert(rs<16);
799 assert(rt<16);
800 assert(shift<16);
801 assem_debug("asr %s,%s,%s\n",regname[rt],regname[rs],regname[shift]);
802 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x50|(shift<<8));
803}
804
805static unused void emit_orrshl(u_int rs,u_int shift,u_int rt)
806{
807 assert(rs<16);
808 assert(rt<16);
809 assert(shift<16);
810 assem_debug("orr %s,%s,%s,lsl %s\n",regname[rt],regname[rt],regname[rs],regname[shift]);
811 output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|0x10|(shift<<8));
812}
813
814static unused void emit_orrshr(u_int rs,u_int shift,u_int rt)
815{
816 assert(rs<16);
817 assert(rt<16);
818 assert(shift<16);
819 assem_debug("orr %s,%s,%s,lsr %s\n",regname[rt],regname[rt],regname[rs],regname[shift]);
820 output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|0x30|(shift<<8));
821}
822
823static void emit_cmpimm(int rs,int imm)
824{
825 u_int armval;
826 if(genimm(imm,&armval)) {
827 assem_debug("cmp %s,#%d\n",regname[rs],imm);
828 output_w32(0xe3500000|rd_rn_rm(0,rs,0)|armval);
829 }else if(genimm(-imm,&armval)) {
830 assem_debug("cmn %s,#%d\n",regname[rs],imm);
831 output_w32(0xe3700000|rd_rn_rm(0,rs,0)|armval);
832 }else if(imm>0) {
833 assert(imm<65536);
834 emit_movimm(imm,HOST_TEMPREG);
835 assem_debug("cmp %s,r14\n",regname[rs]);
836 output_w32(0xe1500000|rd_rn_rm(0,rs,HOST_TEMPREG));
837 }else{
838 assert(imm>-65536);
839 emit_movimm(-imm,HOST_TEMPREG);
840 assem_debug("cmn %s,r14\n",regname[rs]);
841 output_w32(0xe1700000|rd_rn_rm(0,rs,HOST_TEMPREG));
842 }
843}
844
845static void emit_cmovne_imm(int imm,int rt)
846{
847 assem_debug("movne %s,#%d\n",regname[rt],imm);
848 u_int armval;
849 genimm_checked(imm,&armval);
850 output_w32(0x13a00000|rd_rn_rm(rt,0,0)|armval);
851}
852
853static void emit_cmovl_imm(int imm,int rt)
854{
855 assem_debug("movlt %s,#%d\n",regname[rt],imm);
856 u_int armval;
857 genimm_checked(imm,&armval);
858 output_w32(0xb3a00000|rd_rn_rm(rt,0,0)|armval);
859}
860
861static void emit_cmovb_imm(int imm,int rt)
862{
863 assem_debug("movcc %s,#%d\n",regname[rt],imm);
864 u_int armval;
865 genimm_checked(imm,&armval);
866 output_w32(0x33a00000|rd_rn_rm(rt,0,0)|armval);
867}
868
869static void emit_cmovae_imm(int imm,int rt)
870{
871 assem_debug("movcs %s,#%d\n",regname[rt],imm);
872 u_int armval;
873 genimm_checked(imm,&armval);
874 output_w32(0x23a00000|rd_rn_rm(rt,0,0)|armval);
875}
876
877static void emit_cmovs_imm(int imm,int rt)
878{
879 assem_debug("movmi %s,#%d\n",regname[rt],imm);
880 u_int armval;
881 genimm_checked(imm,&armval);
882 output_w32(0x43a00000|rd_rn_rm(rt,0,0)|armval);
883}
884
885static void emit_cmovne_reg(int rs,int rt)
886{
887 assem_debug("movne %s,%s\n",regname[rt],regname[rs]);
888 output_w32(0x11a00000|rd_rn_rm(rt,0,rs));
889}
890
891static void emit_cmovl_reg(int rs,int rt)
892{
893 assem_debug("movlt %s,%s\n",regname[rt],regname[rs]);
894 output_w32(0xb1a00000|rd_rn_rm(rt,0,rs));
895}
896
897static void emit_cmovb_reg(int rs,int rt)
898{
899 assem_debug("movcc %s,%s\n",regname[rt],regname[rs]);
900 output_w32(0x31a00000|rd_rn_rm(rt,0,rs));
901}
902
903static void emit_cmovs_reg(int rs,int rt)
904{
905 assem_debug("movmi %s,%s\n",regname[rt],regname[rs]);
906 output_w32(0x41a00000|rd_rn_rm(rt,0,rs));
907}
908
909static void emit_slti32(int rs,int imm,int rt)
910{
911 if(rs!=rt) emit_zeroreg(rt);
912 emit_cmpimm(rs,imm);
913 if(rs==rt) emit_movimm(0,rt);
914 emit_cmovl_imm(1,rt);
915}
916
917static void emit_sltiu32(int rs,int imm,int rt)
918{
919 if(rs!=rt) emit_zeroreg(rt);
920 emit_cmpimm(rs,imm);
921 if(rs==rt) emit_movimm(0,rt);
922 emit_cmovb_imm(1,rt);
923}
924
925static void emit_cmp(int rs,int rt)
926{
927 assem_debug("cmp %s,%s\n",regname[rs],regname[rt]);
928 output_w32(0xe1500000|rd_rn_rm(0,rs,rt));
929}
930
931static void emit_cmpcs(int rs,int rt)
932{
933 assem_debug("cmpcs %s,%s\n",regname[rs],regname[rt]);
934 output_w32(0x21500000|rd_rn_rm(0,rs,rt));
935}
936
937static void emit_set_gz32(int rs, int rt)
938{
939 //assem_debug("set_gz32\n");
940 emit_cmpimm(rs,1);
941 emit_movimm(1,rt);
942 emit_cmovl_imm(0,rt);
943}
944
945static void emit_set_nz32(int rs, int rt)
946{
947 //assem_debug("set_nz32\n");
948 if(rs!=rt) emit_movs(rs,rt);
949 else emit_test(rs,rs);
950 emit_cmovne_imm(1,rt);
951}
952
953static void emit_set_if_less32(int rs1, int rs2, int rt)
954{
955 //assem_debug("set if less (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]);
956 if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt);
957 emit_cmp(rs1,rs2);
958 if(rs1==rt||rs2==rt) emit_movimm(0,rt);
959 emit_cmovl_imm(1,rt);
960}
961
962static void emit_set_if_carry32(int rs1, int rs2, int rt)
963{
964 //assem_debug("set if carry (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]);
965 if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt);
966 emit_cmp(rs1,rs2);
967 if(rs1==rt||rs2==rt) emit_movimm(0,rt);
968 emit_cmovb_imm(1,rt);
969}
970
971static int can_jump_or_call(const void *a)
972{
973 intptr_t offset = (u_char *)a - out - 8;
974 return (-33554432 <= offset && offset < 33554432);
975}
976
977static void emit_call(const void *a_)
978{
979 int a = (int)a_;
980 assem_debug("bl %x (%x+%x)%s\n",a,(int)out,a-(int)out-8,func_name(a_));
981 u_int offset=genjmp(a);
982 output_w32(0xeb000000|offset);
983}
984
985static void emit_jmp(const void *a_)
986{
987 int a = (int)a_;
988 assem_debug("b %x (%x+%x)%s\n",a,(int)out,a-(int)out-8,func_name(a_));
989 u_int offset=genjmp(a);
990 output_w32(0xea000000|offset);
991}
992
993static void emit_jne(const void *a_)
994{
995 int a = (int)a_;
996 assem_debug("bne %x\n",a);
997 u_int offset=genjmp(a);
998 output_w32(0x1a000000|offset);
999}
1000
1001static void emit_jeq(const void *a_)
1002{
1003 int a = (int)a_;
1004 assem_debug("beq %x\n",a);
1005 u_int offset=genjmp(a);
1006 output_w32(0x0a000000|offset);
1007}
1008
1009static void emit_js(const void *a_)
1010{
1011 int a = (int)a_;
1012 assem_debug("bmi %x\n",a);
1013 u_int offset=genjmp(a);
1014 output_w32(0x4a000000|offset);
1015}
1016
1017static void emit_jns(const void *a_)
1018{
1019 int a = (int)a_;
1020 assem_debug("bpl %x\n",a);
1021 u_int offset=genjmp(a);
1022 output_w32(0x5a000000|offset);
1023}
1024
1025static void emit_jl(const void *a_)
1026{
1027 int a = (int)a_;
1028 assem_debug("blt %x\n",a);
1029 u_int offset=genjmp(a);
1030 output_w32(0xba000000|offset);
1031}
1032
1033static void emit_jge(const void *a_)
1034{
1035 int a = (int)a_;
1036 assem_debug("bge %x\n",a);
1037 u_int offset=genjmp(a);
1038 output_w32(0xaa000000|offset);
1039}
1040
1041static void emit_jo(const void *a_)
1042{
1043 int a = (int)a_;
1044 assem_debug("bvs %x\n",a);
1045 u_int offset=genjmp(a);
1046 output_w32(0x6a000000|offset);
1047}
1048
1049static void emit_jno(const void *a_)
1050{
1051 int a = (int)a_;
1052 assem_debug("bvc %x\n",a);
1053 u_int offset=genjmp(a);
1054 output_w32(0x7a000000|offset);
1055}
1056
1057static void emit_jc(const void *a_)
1058{
1059 int a = (int)a_;
1060 assem_debug("bcs %x\n",a);
1061 u_int offset=genjmp(a);
1062 output_w32(0x2a000000|offset);
1063}
1064
1065static void emit_jcc(const void *a_)
1066{
1067 int a = (int)a_;
1068 assem_debug("bcc %x\n",a);
1069 u_int offset=genjmp(a);
1070 output_w32(0x3a000000|offset);
1071}
1072
1073static void *emit_cbz(int rs, const void *a)
1074{
1075 void *ret;
1076 emit_test(rs, rs);
1077 ret = out;
1078 emit_jeq(a);
1079 return ret;
1080}
1081
1082static unused void emit_callreg(u_int r)
1083{
1084 assert(r<15);
1085 assem_debug("blx %s\n",regname[r]);
1086 output_w32(0xe12fff30|r);
1087}
1088
1089static void emit_jmpreg(u_int r)
1090{
1091 assem_debug("mov pc,%s\n",regname[r]);
1092 output_w32(0xe1a00000|rd_rn_rm(15,0,r));
1093}
1094
1095static void emit_ret(void)
1096{
1097 emit_jmpreg(14);
1098}
1099
1100static void emit_readword_indexed(int offset, int rs, int rt)
1101{
1102 assert(offset>-4096&&offset<4096);
1103 assem_debug("ldr %s,%s+%d\n",regname[rt],regname[rs],offset);
1104 if(offset>=0) {
1105 output_w32(0xe5900000|rd_rn_rm(rt,rs,0)|offset);
1106 }else{
1107 output_w32(0xe5100000|rd_rn_rm(rt,rs,0)|(-offset));
1108 }
1109}
1110
1111static void emit_readword_dualindexedx4(int rs1, int rs2, int rt)
1112{
1113 assem_debug("ldr %s,%s,%s lsl #2\n",regname[rt],regname[rs1],regname[rs2]);
1114 output_w32(0xe7900000|rd_rn_rm(rt,rs1,rs2)|0x100);
1115}
1116#define emit_readptr_dualindexedx_ptrlen emit_readword_dualindexedx4
1117
1118static void emit_ldr_dualindexed(int rs1, int rs2, int rt)
1119{
1120 assem_debug("ldr %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1121 output_w32(0xe7900000|rd_rn_rm(rt,rs1,rs2));
1122}
1123
1124static void emit_ldrcc_dualindexed(int rs1, int rs2, int rt)
1125{
1126 assem_debug("ldrcc %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1127 output_w32(0x37900000|rd_rn_rm(rt,rs1,rs2));
1128}
1129
1130static void emit_ldrb_dualindexed(int rs1, int rs2, int rt)
1131{
1132 assem_debug("ldrb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1133 output_w32(0xe7d00000|rd_rn_rm(rt,rs1,rs2));
1134}
1135
1136static void emit_ldrccb_dualindexed(int rs1, int rs2, int rt)
1137{
1138 assem_debug("ldrccb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1139 output_w32(0x37d00000|rd_rn_rm(rt,rs1,rs2));
1140}
1141
1142static void emit_ldrsb_dualindexed(int rs1, int rs2, int rt)
1143{
1144 assem_debug("ldrsb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1145 output_w32(0xe19000d0|rd_rn_rm(rt,rs1,rs2));
1146}
1147
1148static void emit_ldrccsb_dualindexed(int rs1, int rs2, int rt)
1149{
1150 assem_debug("ldrccsb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1151 output_w32(0x319000d0|rd_rn_rm(rt,rs1,rs2));
1152}
1153
1154static void emit_ldrh_dualindexed(int rs1, int rs2, int rt)
1155{
1156 assem_debug("ldrh %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1157 output_w32(0xe19000b0|rd_rn_rm(rt,rs1,rs2));
1158}
1159
1160static void emit_ldrcch_dualindexed(int rs1, int rs2, int rt)
1161{
1162 assem_debug("ldrcch %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1163 output_w32(0x319000b0|rd_rn_rm(rt,rs1,rs2));
1164}
1165
1166static void emit_ldrsh_dualindexed(int rs1, int rs2, int rt)
1167{
1168 assem_debug("ldrsh %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1169 output_w32(0xe19000f0|rd_rn_rm(rt,rs1,rs2));
1170}
1171
1172static void emit_ldrccsh_dualindexed(int rs1, int rs2, int rt)
1173{
1174 assem_debug("ldrccsh %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1175 output_w32(0x319000f0|rd_rn_rm(rt,rs1,rs2));
1176}
1177
1178static void emit_str_dualindexed(int rs1, int rs2, int rt)
1179{
1180 assem_debug("str %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1181 output_w32(0xe7800000|rd_rn_rm(rt,rs1,rs2));
1182}
1183
1184static void emit_strb_dualindexed(int rs1, int rs2, int rt)
1185{
1186 assem_debug("strb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1187 output_w32(0xe7c00000|rd_rn_rm(rt,rs1,rs2));
1188}
1189
1190static void emit_strh_dualindexed(int rs1, int rs2, int rt)
1191{
1192 assem_debug("strh %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1193 output_w32(0xe18000b0|rd_rn_rm(rt,rs1,rs2));
1194}
1195
1196static void emit_movsbl_indexed(int offset, int rs, int rt)
1197{
1198 assert(offset>-256&&offset<256);
1199 assem_debug("ldrsb %s,%s+%d\n",regname[rt],regname[rs],offset);
1200 if(offset>=0) {
1201 output_w32(0xe1d000d0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
1202 }else{
1203 output_w32(0xe15000d0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
1204 }
1205}
1206
1207static void emit_movswl_indexed(int offset, int rs, int rt)
1208{
1209 assert(offset>-256&&offset<256);
1210 assem_debug("ldrsh %s,%s+%d\n",regname[rt],regname[rs],offset);
1211 if(offset>=0) {
1212 output_w32(0xe1d000f0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
1213 }else{
1214 output_w32(0xe15000f0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
1215 }
1216}
1217
1218static void emit_movzbl_indexed(int offset, int rs, int rt)
1219{
1220 assert(offset>-4096&&offset<4096);
1221 assem_debug("ldrb %s,%s+%d\n",regname[rt],regname[rs],offset);
1222 if(offset>=0) {
1223 output_w32(0xe5d00000|rd_rn_rm(rt,rs,0)|offset);
1224 }else{
1225 output_w32(0xe5500000|rd_rn_rm(rt,rs,0)|(-offset));
1226 }
1227}
1228
1229static void emit_movzwl_indexed(int offset, int rs, int rt)
1230{
1231 assert(offset>-256&&offset<256);
1232 assem_debug("ldrh %s,%s+%d\n",regname[rt],regname[rs],offset);
1233 if(offset>=0) {
1234 output_w32(0xe1d000b0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
1235 }else{
1236 output_w32(0xe15000b0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
1237 }
1238}
1239
1240static void emit_ldrd(int offset, int rs, int rt)
1241{
1242 assert(offset>-256&&offset<256);
1243 assem_debug("ldrd %s,%s+%d\n",regname[rt],regname[rs],offset);
1244 if(offset>=0) {
1245 output_w32(0xe1c000d0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
1246 }else{
1247 output_w32(0xe14000d0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
1248 }
1249}
1250
1251static void emit_readword(void *addr, int rt)
1252{
1253 uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
1254 assert(offset<4096);
1255 assem_debug("ldr %s,fp+%#x%s\n", regname[rt], offset, fpofs_name(offset));
1256 output_w32(0xe5900000|rd_rn_rm(rt,FP,0)|offset);
1257}
1258#define emit_readptr emit_readword
1259
1260static void emit_writeword_indexed(int rt, int offset, int rs)
1261{
1262 assert(offset>-4096&&offset<4096);
1263 assem_debug("str %s,%s+%d\n",regname[rt],regname[rs],offset);
1264 if(offset>=0) {
1265 output_w32(0xe5800000|rd_rn_rm(rt,rs,0)|offset);
1266 }else{
1267 output_w32(0xe5000000|rd_rn_rm(rt,rs,0)|(-offset));
1268 }
1269}
1270
1271static void emit_writehword_indexed(int rt, int offset, int rs)
1272{
1273 assert(offset>-256&&offset<256);
1274 assem_debug("strh %s,%s+%d\n",regname[rt],regname[rs],offset);
1275 if(offset>=0) {
1276 output_w32(0xe1c000b0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
1277 }else{
1278 output_w32(0xe14000b0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
1279 }
1280}
1281
1282static void emit_writebyte_indexed(int rt, int offset, int rs)
1283{
1284 assert(offset>-4096&&offset<4096);
1285 assem_debug("strb %s,%s+%d\n",regname[rt],regname[rs],offset);
1286 if(offset>=0) {
1287 output_w32(0xe5c00000|rd_rn_rm(rt,rs,0)|offset);
1288 }else{
1289 output_w32(0xe5400000|rd_rn_rm(rt,rs,0)|(-offset));
1290 }
1291}
1292
1293static void emit_strcc_dualindexed(int rs1, int rs2, int rt)
1294{
1295 assem_debug("strcc %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1296 output_w32(0x37800000|rd_rn_rm(rt,rs1,rs2));
1297}
1298
1299static void emit_strccb_dualindexed(int rs1, int rs2, int rt)
1300{
1301 assem_debug("strccb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1302 output_w32(0x37c00000|rd_rn_rm(rt,rs1,rs2));
1303}
1304
1305static void emit_strcch_dualindexed(int rs1, int rs2, int rt)
1306{
1307 assem_debug("strcch %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1308 output_w32(0x318000b0|rd_rn_rm(rt,rs1,rs2));
1309}
1310
1311static void emit_writeword(int rt, void *addr)
1312{
1313 uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
1314 assert(offset<4096);
1315 assem_debug("str %s,fp+%#x%s\n", regname[rt], offset, fpofs_name(offset));
1316 output_w32(0xe5800000|rd_rn_rm(rt,FP,0)|offset);
1317}
1318
1319static void emit_umull(u_int rs1, u_int rs2, u_int hi, u_int lo)
1320{
1321 assem_debug("umull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]);
1322 assert(rs1<16);
1323 assert(rs2<16);
1324 assert(hi<16);
1325 assert(lo<16);
1326 output_w32(0xe0800090|(hi<<16)|(lo<<12)|(rs2<<8)|rs1);
1327}
1328
1329static void emit_smull(u_int rs1, u_int rs2, u_int hi, u_int lo)
1330{
1331 assem_debug("smull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]);
1332 assert(rs1<16);
1333 assert(rs2<16);
1334 assert(hi<16);
1335 assert(lo<16);
1336 output_w32(0xe0c00090|(hi<<16)|(lo<<12)|(rs2<<8)|rs1);
1337}
1338
1339static void emit_clz(int rs,int rt)
1340{
1341 assem_debug("clz %s,%s\n",regname[rt],regname[rs]);
1342 output_w32(0xe16f0f10|rd_rn_rm(rt,0,rs));
1343}
1344
1345static void emit_subcs(int rs1,int rs2,int rt)
1346{
1347 assem_debug("subcs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
1348 output_w32(0x20400000|rd_rn_rm(rt,rs1,rs2));
1349}
1350
1351static void emit_shrcc_imm(int rs,u_int imm,int rt)
1352{
1353 assert(imm>0);
1354 assert(imm<32);
1355 assem_debug("lsrcc %s,%s,#%d\n",regname[rt],regname[rs],imm);
1356 output_w32(0x31a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7));
1357}
1358
1359static void emit_shrne_imm(int rs,u_int imm,int rt)
1360{
1361 assert(imm>0);
1362 assert(imm<32);
1363 assem_debug("lsrne %s,%s,#%d\n",regname[rt],regname[rs],imm);
1364 output_w32(0x11a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7));
1365}
1366
1367static void emit_negmi(int rs, int rt)
1368{
1369 assem_debug("rsbmi %s,%s,#0\n",regname[rt],regname[rs]);
1370 output_w32(0x42600000|rd_rn_rm(rt,rs,0));
1371}
1372
1373static void emit_negsmi(int rs, int rt)
1374{
1375 assem_debug("rsbsmi %s,%s,#0\n",regname[rt],regname[rs]);
1376 output_w32(0x42700000|rd_rn_rm(rt,rs,0));
1377}
1378
1379static void emit_bic_lsl(u_int rs1,u_int rs2,u_int shift,u_int rt)
1380{
1381 assem_debug("bic %s,%s,%s lsl %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]);
1382 output_w32(0xe1C00000|rd_rn_rm(rt,rs1,rs2)|0x10|(shift<<8));
1383}
1384
1385static void emit_bic_lsr(u_int rs1,u_int rs2,u_int shift,u_int rt)
1386{
1387 assem_debug("bic %s,%s,%s lsr %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]);
1388 output_w32(0xe1C00000|rd_rn_rm(rt,rs1,rs2)|0x30|(shift<<8));
1389}
1390
1391static void emit_teq(int rs, int rt)
1392{
1393 assem_debug("teq %s,%s\n",regname[rs],regname[rt]);
1394 output_w32(0xe1300000|rd_rn_rm(0,rs,rt));
1395}
1396
1397static unused void emit_rsbimm(int rs, int imm, int rt)
1398{
1399 u_int armval;
1400 genimm_checked(imm,&armval);
1401 assem_debug("rsb %s,%s,#%d\n",regname[rt],regname[rs],imm);
1402 output_w32(0xe2600000|rd_rn_rm(rt,rs,0)|armval);
1403}
1404
1405// Conditionally select one of two immediates, optimizing for small code size
1406// This will only be called if HAVE_CMOV_IMM is defined
1407static void emit_cmov2imm_e_ne_compact(int imm1,int imm2,u_int rt)
1408{
1409 u_int armval;
1410 if(genimm(imm2-imm1,&armval)) {
1411 emit_movimm(imm1,rt);
1412 assem_debug("addne %s,%s,#%d\n",regname[rt],regname[rt],imm2-imm1);
1413 output_w32(0x12800000|rd_rn_rm(rt,rt,0)|armval);
1414 }else if(genimm(imm1-imm2,&armval)) {
1415 emit_movimm(imm1,rt);
1416 assem_debug("subne %s,%s,#%d\n",regname[rt],regname[rt],imm1-imm2);
1417 output_w32(0x12400000|rd_rn_rm(rt,rt,0)|armval);
1418 }
1419 else {
1420 #ifndef HAVE_ARMV7
1421 emit_movimm(imm1,rt);
1422 add_literal((int)out,imm2);
1423 assem_debug("ldrne %s,pc+? [=%x]\n",regname[rt],imm2);
1424 output_w32(0x15900000|rd_rn_rm(rt,15,0));
1425 #else
1426 emit_movw(imm1&0x0000FFFF,rt);
1427 if((imm1&0xFFFF)!=(imm2&0xFFFF)) {
1428 assem_debug("movwne %s,#%d (0x%x)\n",regname[rt],imm2&0xFFFF,imm2&0xFFFF);
1429 output_w32(0x13000000|rd_rn_rm(rt,0,0)|(imm2&0xfff)|((imm2<<4)&0xf0000));
1430 }
1431 emit_movt(imm1&0xFFFF0000,rt);
1432 if((imm1&0xFFFF0000)!=(imm2&0xFFFF0000)) {
1433 assem_debug("movtne %s,#%d (0x%x)\n",regname[rt],imm2&0xffff0000,imm2&0xffff0000);
1434 output_w32(0x13400000|rd_rn_rm(rt,0,0)|((imm2>>16)&0xfff)|((imm2>>12)&0xf0000));
1435 }
1436 #endif
1437 }
1438}
1439
1440// special case for checking invalid_code
1441static void emit_ldrb_indexedsr12_reg(int base, int r, int rt)
1442{
1443 assem_debug("ldrb %s,%s,%s lsr #12\n",regname[rt],regname[base],regname[r]);
1444 output_w32(0xe7d00000|rd_rn_rm(rt,base,r)|0x620);
1445}
1446
1447static void emit_callne(int a)
1448{
1449 assem_debug("blne %x\n",a);
1450 u_int offset=genjmp(a);
1451 output_w32(0x1b000000|offset);
1452}
1453
1454// Used to preload hash table entries
1455static unused void emit_prefetchreg(int r)
1456{
1457 assem_debug("pld %s\n",regname[r]);
1458 output_w32(0xf5d0f000|rd_rn_rm(0,r,0));
1459}
1460
1461// Special case for mini_ht
1462static void emit_ldreq_indexed(int rs, u_int offset, int rt)
1463{
1464 assert(offset<4096);
1465 assem_debug("ldreq %s,[%s, #%d]\n",regname[rt],regname[rs],offset);
1466 output_w32(0x05900000|rd_rn_rm(rt,rs,0)|offset);
1467}
1468
1469static void emit_orrne_imm(int rs,int imm,int rt)
1470{
1471 u_int armval;
1472 genimm_checked(imm,&armval);
1473 assem_debug("orrne %s,%s,#%d\n",regname[rt],regname[rs],imm);
1474 output_w32(0x13800000|rd_rn_rm(rt,rs,0)|armval);
1475}
1476
1477static unused void emit_addpl_imm(int rs,int imm,int rt)
1478{
1479 u_int armval;
1480 genimm_checked(imm,&armval);
1481 assem_debug("addpl %s,%s,#%d\n",regname[rt],regname[rs],imm);
1482 output_w32(0x52800000|rd_rn_rm(rt,rs,0)|armval);
1483}
1484
1485static void emit_jno_unlikely(int a)
1486{
1487 //emit_jno(a);
1488 assem_debug("addvc pc,pc,#? (%x)\n",/*a-(int)out-8,*/a);
1489 output_w32(0x72800000|rd_rn_rm(15,15,0));
1490}
1491
1492static void save_regs_all(u_int reglist)
1493{
1494 int i;
1495 if(!reglist) return;
1496 assem_debug("stmia fp,{");
1497 for(i=0;i<16;i++)
1498 if(reglist&(1<<i))
1499 assem_debug("r%d,",i);
1500 assem_debug("}\n");
1501 output_w32(0xe88b0000|reglist);
1502}
1503
1504static void restore_regs_all(u_int reglist)
1505{
1506 int i;
1507 if(!reglist) return;
1508 assem_debug("ldmia fp,{");
1509 for(i=0;i<16;i++)
1510 if(reglist&(1<<i))
1511 assem_debug("r%d,",i);
1512 assem_debug("}\n");
1513 output_w32(0xe89b0000|reglist);
1514}
1515
1516// Save registers before function call
1517static void save_regs(u_int reglist)
1518{
1519 reglist&=CALLER_SAVE_REGS; // only save the caller-save registers, r0-r3, r12
1520 save_regs_all(reglist);
1521}
1522
1523// Restore registers after function call
1524static void restore_regs(u_int reglist)
1525{
1526 reglist&=CALLER_SAVE_REGS;
1527 restore_regs_all(reglist);
1528}
1529
1530/* Stubs/epilogue */
1531
1532static void literal_pool(int n)
1533{
1534 if(!literalcount) return;
1535 if(n) {
1536 if((int)out-literals[0][0]<4096-n) return;
1537 }
1538 u_int *ptr;
1539 int i;
1540 for(i=0;i<literalcount;i++)
1541 {
1542 u_int l_addr=(u_int)out;
1543 int j;
1544 for(j=0;j<i;j++) {
1545 if(literals[j][1]==literals[i][1]) {
1546 //printf("dup %08x\n",literals[i][1]);
1547 l_addr=literals[j][0];
1548 break;
1549 }
1550 }
1551 ptr=(u_int *)literals[i][0];
1552 u_int offset=l_addr-(u_int)ptr-8;
1553 assert(offset<4096);
1554 assert(!(offset&3));
1555 *ptr|=offset;
1556 if(l_addr==(u_int)out) {
1557 literals[i][0]=l_addr; // remember for dupes
1558 output_w32(literals[i][1]);
1559 }
1560 }
1561 literalcount=0;
1562}
1563
1564static void literal_pool_jumpover(int n)
1565{
1566 if(!literalcount) return;
1567 if(n) {
1568 if((int)out-literals[0][0]<4096-n) return;
1569 }
1570 void *jaddr = out;
1571 emit_jmp(0);
1572 literal_pool(0);
1573 set_jump_target(jaddr, out);
1574}
1575
1576// parsed by get_pointer, find_extjump_insn
1577static void emit_extjump(u_char *addr, u_int target)
1578{
1579 u_char *ptr=(u_char *)addr;
1580 assert((ptr[3]&0x0e)==0xa);
1581 (void)ptr;
1582
1583 emit_loadlp(target,0);
1584 emit_loadlp((u_int)addr,1);
1585 assert(ndrc->translation_cache <= addr &&
1586 addr < ndrc->translation_cache + sizeof(ndrc->translation_cache));
1587 emit_far_jump(dyna_linker);
1588}
1589
1590static void check_extjump2(void *src)
1591{
1592 u_int *ptr = src;
1593 assert((ptr[1] & 0x0fff0000) == 0x059f0000); // ldr rx, [pc, #ofs]
1594 (void)ptr;
1595}
1596
1597// put rt_val into rt, potentially making use of rs with value rs_val
1598static void emit_movimm_from(u_int rs_val,int rs,u_int rt_val,int rt)
1599{
1600 u_int armval;
1601 int diff;
1602 if(genimm(rt_val,&armval)) {
1603 assem_debug("mov %s,#%d\n",regname[rt],rt_val);
1604 output_w32(0xe3a00000|rd_rn_rm(rt,0,0)|armval);
1605 return;
1606 }
1607 if(genimm(~rt_val,&armval)) {
1608 assem_debug("mvn %s,#%d\n",regname[rt],rt_val);
1609 output_w32(0xe3e00000|rd_rn_rm(rt,0,0)|armval);
1610 return;
1611 }
1612 diff=rt_val-rs_val;
1613 if(genimm(diff,&armval)) {
1614 assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],diff);
1615 output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
1616 return;
1617 }else if(genimm(-diff,&armval)) {
1618 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-diff);
1619 output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
1620 return;
1621 }
1622 emit_movimm(rt_val,rt);
1623}
1624
1625// return 1 if above function can do it's job cheaply
1626static int is_similar_value(u_int v1,u_int v2)
1627{
1628 u_int xs;
1629 int diff;
1630 if(v1==v2) return 1;
1631 diff=v2-v1;
1632 for(xs=diff;xs!=0&&(xs&3)==0;xs>>=2)
1633 ;
1634 if(xs<0x100) return 1;
1635 for(xs=-diff;xs!=0&&(xs&3)==0;xs>>=2)
1636 ;
1637 if(xs<0x100) return 1;
1638 return 0;
1639}
1640
1641static void mov_loadtype_adj(enum stub_type type,int rs,int rt)
1642{
1643 switch(type) {
1644 case LOADB_STUB: emit_signextend8(rs,rt); break;
1645 case LOADBU_STUB: emit_andimm(rs,0xff,rt); break;
1646 case LOADH_STUB: emit_signextend16(rs,rt); break;
1647 case LOADHU_STUB: emit_andimm(rs,0xffff,rt); break;
1648 case LOADW_STUB: if(rs!=rt) emit_mov(rs,rt); break;
1649 default: assert(0);
1650 }
1651}
1652
1653#include "pcsxmem.h"
1654#include "pcsxmem_inline.c"
1655
1656static void do_readstub(int n)
1657{
1658 assem_debug("do_readstub %x\n",start+stubs[n].a*4);
1659 literal_pool(256);
1660 set_jump_target(stubs[n].addr, out);
1661 enum stub_type type=stubs[n].type;
1662 int i=stubs[n].a;
1663 int rs=stubs[n].b;
1664 const struct regstat *i_regs=(struct regstat *)stubs[n].c;
1665 u_int reglist=stubs[n].e;
1666 const signed char *i_regmap=i_regs->regmap;
1667 int rt;
1668 if(dops[i].itype==C2LS||dops[i].itype==LOADLR) {
1669 rt=get_reg(i_regmap,FTEMP);
1670 }else{
1671 rt=get_reg(i_regmap,dops[i].rt1);
1672 }
1673 assert(rs>=0);
1674 int r,temp=-1,temp2=HOST_TEMPREG,regs_saved=0;
1675 void *restore_jump = NULL;
1676 reglist|=(1<<rs);
1677 for(r=0;r<=12;r++) {
1678 if(((1<<r)&0x13ff)&&((1<<r)&reglist)==0) {
1679 temp=r; break;
1680 }
1681 }
1682 if(rt>=0&&dops[i].rt1!=0)
1683 reglist&=~(1<<rt);
1684 if(temp==-1) {
1685 save_regs(reglist);
1686 regs_saved=1;
1687 temp=(rs==0)?2:0;
1688 }
1689 if((regs_saved||(reglist&2)==0)&&temp!=1&&rs!=1)
1690 temp2=1;
1691 emit_readword(&mem_rtab,temp);
1692 emit_shrimm(rs,12,temp2);
1693 emit_readword_dualindexedx4(temp,temp2,temp2);
1694 emit_lsls_imm(temp2,1,temp2);
1695 if(dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) {
1696 switch(type) {
1697 case LOADB_STUB: emit_ldrccsb_dualindexed(temp2,rs,rt); break;
1698 case LOADBU_STUB: emit_ldrccb_dualindexed(temp2,rs,rt); break;
1699 case LOADH_STUB: emit_ldrccsh_dualindexed(temp2,rs,rt); break;
1700 case LOADHU_STUB: emit_ldrcch_dualindexed(temp2,rs,rt); break;
1701 case LOADW_STUB: emit_ldrcc_dualindexed(temp2,rs,rt); break;
1702 default: assert(0);
1703 }
1704 }
1705 if(regs_saved) {
1706 restore_jump=out;
1707 emit_jcc(0); // jump to reg restore
1708 }
1709 else
1710 emit_jcc(stubs[n].retaddr); // return address
1711
1712 if(!regs_saved)
1713 save_regs(reglist);
1714 void *handler=NULL;
1715 if(type==LOADB_STUB||type==LOADBU_STUB)
1716 handler=jump_handler_read8;
1717 if(type==LOADH_STUB||type==LOADHU_STUB)
1718 handler=jump_handler_read16;
1719 if(type==LOADW_STUB)
1720 handler=jump_handler_read32;
1721 assert(handler);
1722 pass_args(rs,temp2);
1723 int cc=get_reg(i_regmap,CCREG);
1724 if(cc<0)
1725 emit_loadreg(CCREG,2);
1726 emit_addimm(cc<0?2:cc,(int)stubs[n].d,2);
1727 emit_far_call(handler);
1728 if(dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) {
1729 mov_loadtype_adj(type,0,rt);
1730 }
1731 if(restore_jump)
1732 set_jump_target(restore_jump, out);
1733 restore_regs(reglist);
1734 emit_jmp(stubs[n].retaddr); // return address
1735}
1736
1737static void inline_readstub(enum stub_type type, int i, u_int addr,
1738 const signed char regmap[], int target, int adj, u_int reglist)
1739{
1740 int ra = cinfo[i].addr;
1741 int rt = get_reg(regmap,target);
1742 assert(ra >= 0);
1743 u_int is_dynamic;
1744 uintptr_t host_addr = 0;
1745 void *handler;
1746 int cc=get_reg(regmap,CCREG);
1747 if(pcsx_direct_read(type,addr,adj,cc,target?ra:-1,rt))
1748 return;
1749 handler = get_direct_memhandler(mem_rtab, addr, type, &host_addr);
1750 if (handler == NULL) {
1751 if(rt<0||dops[i].rt1==0)
1752 return;
1753 if(addr!=host_addr)
1754 emit_movimm_from(addr,ra,host_addr,ra);
1755 switch(type) {
1756 case LOADB_STUB: emit_movsbl_indexed(0,ra,rt); break;
1757 case LOADBU_STUB: emit_movzbl_indexed(0,ra,rt); break;
1758 case LOADH_STUB: emit_movswl_indexed(0,ra,rt); break;
1759 case LOADHU_STUB: emit_movzwl_indexed(0,ra,rt); break;
1760 case LOADW_STUB: emit_readword_indexed(0,ra,rt); break;
1761 default: assert(0);
1762 }
1763 return;
1764 }
1765 is_dynamic=pcsxmem_is_handler_dynamic(addr);
1766 if(is_dynamic) {
1767 if(type==LOADB_STUB||type==LOADBU_STUB)
1768 handler=jump_handler_read8;
1769 if(type==LOADH_STUB||type==LOADHU_STUB)
1770 handler=jump_handler_read16;
1771 if(type==LOADW_STUB)
1772 handler=jump_handler_read32;
1773 }
1774
1775 // call a memhandler
1776 if(rt>=0&&dops[i].rt1!=0)
1777 reglist&=~(1<<rt);
1778 save_regs(reglist);
1779 if(target==0)
1780 emit_movimm(addr,0);
1781 else if(ra!=0)
1782 emit_mov(ra,0);
1783 if(cc<0)
1784 emit_loadreg(CCREG,2);
1785 if(is_dynamic) {
1786 emit_movimm(((u_int *)mem_rtab)[addr>>12]<<1,1);
1787 emit_addimm(cc<0?2:cc,adj,2);
1788 }
1789 else {
1790 emit_readword(&last_count,3);
1791 emit_addimm(cc<0?2:cc,adj,2);
1792 emit_add(2,3,2);
1793 emit_writeword(2,&psxRegs.cycle);
1794 }
1795
1796 emit_far_call(handler);
1797
1798 if(rt>=0&&dops[i].rt1!=0) {
1799 switch(type) {
1800 case LOADB_STUB: emit_signextend8(0,rt); break;
1801 case LOADBU_STUB: emit_andimm(0,0xff,rt); break;
1802 case LOADH_STUB: emit_signextend16(0,rt); break;
1803 case LOADHU_STUB: emit_andimm(0,0xffff,rt); break;
1804 case LOADW_STUB: if(rt!=0) emit_mov(0,rt); break;
1805 default: assert(0);
1806 }
1807 }
1808 restore_regs(reglist);
1809}
1810
1811static void do_writestub(int n)
1812{
1813 assem_debug("do_writestub %x\n",start+stubs[n].a*4);
1814 literal_pool(256);
1815 set_jump_target(stubs[n].addr, out);
1816 enum stub_type type=stubs[n].type;
1817 int i=stubs[n].a;
1818 int rs=stubs[n].b;
1819 const struct regstat *i_regs=(struct regstat *)stubs[n].c;
1820 u_int reglist=stubs[n].e;
1821 const signed char *i_regmap=i_regs->regmap;
1822 int rt,r;
1823 if(dops[i].itype==C2LS) {
1824 rt=get_reg(i_regmap,r=FTEMP);
1825 }else{
1826 rt=get_reg(i_regmap,r=dops[i].rs2);
1827 }
1828 assert(rs>=0);
1829 assert(rt>=0);
1830 int rtmp,temp=-1,temp2=HOST_TEMPREG,regs_saved=0;
1831 void *restore_jump = NULL;
1832 int reglist2=reglist|(1<<rs)|(1<<rt);
1833 for(rtmp=0;rtmp<=12;rtmp++) {
1834 if(((1<<rtmp)&0x13ff)&&((1<<rtmp)&reglist2)==0) {
1835 temp=rtmp; break;
1836 }
1837 }
1838 if(temp==-1) {
1839 save_regs(reglist);
1840 regs_saved=1;
1841 for(rtmp=0;rtmp<=3;rtmp++)
1842 if(rtmp!=rs&&rtmp!=rt)
1843 {temp=rtmp;break;}
1844 }
1845 if((regs_saved||(reglist2&8)==0)&&temp!=3&&rs!=3&&rt!=3)
1846 temp2=3;
1847 emit_readword(&mem_wtab,temp);
1848 emit_shrimm(rs,12,temp2);
1849 emit_readword_dualindexedx4(temp,temp2,temp2);
1850 emit_lsls_imm(temp2,1,temp2);
1851 switch(type) {
1852 case STOREB_STUB: emit_strccb_dualindexed(temp2,rs,rt); break;
1853 case STOREH_STUB: emit_strcch_dualindexed(temp2,rs,rt); break;
1854 case STOREW_STUB: emit_strcc_dualindexed(temp2,rs,rt); break;
1855 default: assert(0);
1856 }
1857 if(regs_saved) {
1858 restore_jump=out;
1859 emit_jcc(0); // jump to reg restore
1860 }
1861 else
1862 emit_jcc(stubs[n].retaddr); // return address (invcode check)
1863
1864 if(!regs_saved)
1865 save_regs(reglist);
1866 void *handler=NULL;
1867 switch(type) {
1868 case STOREB_STUB: handler=jump_handler_write8; break;
1869 case STOREH_STUB: handler=jump_handler_write16; break;
1870 case STOREW_STUB: handler=jump_handler_write32; break;
1871 default: assert(0);
1872 }
1873 assert(handler);
1874 pass_args(rs,rt);
1875 if(temp2!=3)
1876 emit_mov(temp2,3);
1877 int cc=get_reg(i_regmap,CCREG);
1878 if(cc<0)
1879 emit_loadreg(CCREG,2);
1880 emit_addimm(cc<0?2:cc,(int)stubs[n].d,2);
1881 // returns new cycle_count
1882 emit_far_call(handler);
1883 emit_addimm(0,-(int)stubs[n].d,cc<0?2:cc);
1884 if(cc<0)
1885 emit_storereg(CCREG,2);
1886 if(restore_jump)
1887 set_jump_target(restore_jump, out);
1888 restore_regs(reglist);
1889 emit_jmp(stubs[n].retaddr);
1890}
1891
1892static void inline_writestub(enum stub_type type, int i, u_int addr,
1893 const signed char regmap[], int target, int adj, u_int reglist)
1894{
1895 int ra = cinfo[i].addr;
1896 int rt = get_reg(regmap, target);
1897 assert(ra>=0);
1898 assert(rt>=0);
1899 uintptr_t host_addr = 0;
1900 void *handler = get_direct_memhandler(mem_wtab, addr, type, &host_addr);
1901 if (handler == NULL) {
1902 if(addr!=host_addr)
1903 emit_movimm_from(addr,ra,host_addr,ra);
1904 switch(type) {
1905 case STOREB_STUB: emit_writebyte_indexed(rt,0,ra); break;
1906 case STOREH_STUB: emit_writehword_indexed(rt,0,ra); break;
1907 case STOREW_STUB: emit_writeword_indexed(rt,0,ra); break;
1908 default: assert(0);
1909 }
1910 return;
1911 }
1912
1913 // call a memhandler
1914 save_regs(reglist);
1915 pass_args(ra,rt);
1916 int cc=get_reg(regmap,CCREG);
1917 if(cc<0)
1918 emit_loadreg(CCREG,2);
1919 emit_addimm(cc<0?2:cc,adj,2);
1920 emit_movimm((u_int)handler,3);
1921 // returns new cycle_count
1922 emit_far_call(jump_handler_write_h);
1923 emit_addimm(0,-adj,cc<0?2:cc);
1924 if(cc<0)
1925 emit_storereg(CCREG,2);
1926 restore_regs(reglist);
1927}
1928
1929/* Special assem */
1930
1931static void c2op_prologue(u_int op, int i, const struct regstat *i_regs, u_int reglist)
1932{
1933 save_regs_all(reglist);
1934 cop2_do_stall_check(op, i, i_regs, 0);
1935#ifdef PCNT
1936 emit_movimm(op, 0);
1937 emit_far_call(pcnt_gte_start);
1938#endif
1939 emit_addimm(FP, (u_char *)&psxRegs.CP2D.r[0] - (u_char *)&dynarec_local, 0); // cop2 regs
1940}
1941
1942static void c2op_epilogue(u_int op,u_int reglist)
1943{
1944#ifdef PCNT
1945 emit_movimm(op,0);
1946 emit_far_call(pcnt_gte_end);
1947#endif
1948 restore_regs_all(reglist);
1949}
1950
1951static void c2op_call_MACtoIR(int lm,int need_flags)
1952{
1953 if(need_flags)
1954 emit_far_call(lm?gteMACtoIR_lm1:gteMACtoIR_lm0);
1955 else
1956 emit_far_call(lm?gteMACtoIR_lm1_nf:gteMACtoIR_lm0_nf);
1957}
1958
1959static void c2op_call_rgb_func(void *func,int lm,int need_ir,int need_flags)
1960{
1961 emit_far_call(func);
1962 // func is C code and trashes r0
1963 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
1964 if(need_flags||need_ir)
1965 c2op_call_MACtoIR(lm,need_flags);
1966 emit_far_call(need_flags?gteMACtoRGB:gteMACtoRGB_nf);
1967}
1968
1969static void c2op_assemble(int i, const struct regstat *i_regs)
1970{
1971 u_int c2op = source[i] & 0x3f;
1972 u_int reglist_full = get_host_reglist(i_regs->regmap);
1973 u_int reglist = reglist_full & CALLER_SAVE_REGS;
1974 int need_flags, need_ir;
1975
1976 if (gte_handlers[c2op]!=NULL) {
1977 need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
1978 need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
1979 assem_debug("gte op %08x, unneeded %016llx, need_flags %d, need_ir %d\n",
1980 source[i],gte_unneeded[i+1],need_flags,need_ir);
1981 if(HACK_ENABLED(NDHACK_GTE_NO_FLAGS))
1982 need_flags=0;
1983 int shift = (source[i] >> 19) & 1;
1984 int lm = (source[i] >> 10) & 1;
1985 switch(c2op) {
1986#ifndef DRC_DBG
1987 case GTE_MVMVA: {
1988#ifdef HAVE_ARMV5
1989 int v = (source[i] >> 15) & 3;
1990 int cv = (source[i] >> 13) & 3;
1991 int mx = (source[i] >> 17) & 3;
1992 reglist=reglist_full&(CALLER_SAVE_REGS|0xf0); // +{r4-r7}
1993 c2op_prologue(c2op,i,i_regs,reglist);
1994 /* r4,r5 = VXYZ(v) packed; r6 = &MX11(mx); r7 = &CV1(cv) */
1995 if(v<3)
1996 emit_ldrd(v*8,0,4);
1997 else {
1998 emit_movzwl_indexed(9*4,0,4); // gteIR
1999 emit_movzwl_indexed(10*4,0,6);
2000 emit_movzwl_indexed(11*4,0,5);
2001 emit_orrshl_imm(6,16,4);
2002 }
2003 if(mx<3)
2004 emit_addimm(0,32*4+mx*8*4,6);
2005 else
2006 emit_readword(&zeromem_ptr,6);
2007 if(cv<3)
2008 emit_addimm(0,32*4+(cv*8+5)*4,7);
2009 else
2010 emit_readword(&zeromem_ptr,7);
2011#ifdef __ARM_NEON__
2012 emit_movimm(source[i],1); // opcode
2013 emit_far_call(gteMVMVA_part_neon);
2014 if(need_flags) {
2015 emit_movimm(lm,1);
2016 emit_far_call(gteMACtoIR_flags_neon);
2017 }
2018#else
2019 if(cv==3&&shift)
2020 emit_far_call(gteMVMVA_part_cv3sh12_arm);
2021 else {
2022 emit_movimm(shift,1);
2023 emit_far_call(need_flags?gteMVMVA_part_arm:gteMVMVA_part_nf_arm);
2024 }
2025 if(need_flags||need_ir)
2026 c2op_call_MACtoIR(lm,need_flags);
2027#endif
2028#else /* if not HAVE_ARMV5 */
2029 c2op_prologue(c2op,i,i_regs,reglist);
2030 emit_movimm(source[i],1); // opcode
2031 emit_writeword(1,&psxRegs.code);
2032 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
2033#endif
2034 break;
2035 }
2036 case GTE_OP:
2037 c2op_prologue(c2op,i,i_regs,reglist);
2038 emit_far_call(shift?gteOP_part_shift:gteOP_part_noshift);
2039 if(need_flags||need_ir) {
2040 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
2041 c2op_call_MACtoIR(lm,need_flags);
2042 }
2043 break;
2044 case GTE_DPCS:
2045 c2op_prologue(c2op,i,i_regs,reglist);
2046 c2op_call_rgb_func(shift?gteDPCS_part_shift:gteDPCS_part_noshift,lm,need_ir,need_flags);
2047 break;
2048 case GTE_INTPL:
2049 c2op_prologue(c2op,i,i_regs,reglist);
2050 c2op_call_rgb_func(shift?gteINTPL_part_shift:gteINTPL_part_noshift,lm,need_ir,need_flags);
2051 break;
2052 case GTE_SQR:
2053 c2op_prologue(c2op,i,i_regs,reglist);
2054 emit_far_call(shift?gteSQR_part_shift:gteSQR_part_noshift);
2055 if(need_flags||need_ir) {
2056 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
2057 c2op_call_MACtoIR(lm,need_flags);
2058 }
2059 break;
2060 case GTE_DCPL:
2061 c2op_prologue(c2op,i,i_regs,reglist);
2062 c2op_call_rgb_func(gteDCPL_part,lm,need_ir,need_flags);
2063 break;
2064 case GTE_GPF:
2065 c2op_prologue(c2op,i,i_regs,reglist);
2066 c2op_call_rgb_func(shift?gteGPF_part_shift:gteGPF_part_noshift,lm,need_ir,need_flags);
2067 break;
2068 case GTE_GPL:
2069 c2op_prologue(c2op,i,i_regs,reglist);
2070 c2op_call_rgb_func(shift?gteGPL_part_shift:gteGPL_part_noshift,lm,need_ir,need_flags);
2071 break;
2072#endif
2073 default:
2074 c2op_prologue(c2op,i,i_regs,reglist);
2075#ifdef DRC_DBG
2076 emit_movimm(source[i],1); // opcode
2077 emit_writeword(1,&psxRegs.code);
2078#endif
2079 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
2080 break;
2081 }
2082 c2op_epilogue(c2op,reglist);
2083 }
2084}
2085
2086static void c2op_ctc2_31_assemble(signed char sl, signed char temp)
2087{
2088 //value = value & 0x7ffff000;
2089 //if (value & 0x7f87e000) value |= 0x80000000;
2090 emit_shrimm(sl,12,temp);
2091 emit_shlimm(temp,12,temp);
2092 emit_testimm(temp,0x7f000000);
2093 emit_testeqimm(temp,0x00870000);
2094 emit_testeqimm(temp,0x0000e000);
2095 emit_orrne_imm(temp,0x80000000,temp);
2096}
2097
2098static void do_mfc2_31_one(u_int copr,signed char temp)
2099{
2100 emit_readword(&reg_cop2d[copr],temp);
2101 emit_lsls_imm(temp,16,temp);
2102 emit_cmovs_imm(0,temp);
2103 emit_cmpimm(temp,0xf80<<16);
2104 emit_andimm(temp,0xf80<<16,temp);
2105 emit_cmovae_imm(0xf80<<16,temp);
2106}
2107
2108static void c2op_mfc2_29_assemble(signed char tl, signed char temp)
2109{
2110 if (temp < 0) {
2111 host_tempreg_acquire();
2112 temp = HOST_TEMPREG;
2113 }
2114 do_mfc2_31_one(9,temp);
2115 emit_shrimm(temp,7+16,tl);
2116 do_mfc2_31_one(10,temp);
2117 emit_orrshr_imm(temp,2+16,tl);
2118 do_mfc2_31_one(11,temp);
2119 emit_orrshr_imm(temp,-3+16,tl);
2120 emit_writeword(tl,&reg_cop2d[29]);
2121 if (temp == HOST_TEMPREG)
2122 host_tempreg_release();
2123}
2124
2125static void multdiv_assemble_arm(int i, const struct regstat *i_regs)
2126{
2127 // case 0x18: MULT
2128 // case 0x19: MULTU
2129 // case 0x1A: DIV
2130 // case 0x1B: DIVU
2131 if(dops[i].rs1&&dops[i].rs2)
2132 {
2133 switch (dops[i].opcode2)
2134 {
2135 case 0x18: // MULT
2136 {
2137 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2138 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
2139 signed char hi=get_reg(i_regs->regmap,HIREG);
2140 signed char lo=get_reg(i_regs->regmap,LOREG);
2141 assert(m1>=0);
2142 assert(m2>=0);
2143 assert(hi>=0);
2144 assert(lo>=0);
2145 emit_smull(m1,m2,hi,lo);
2146 }
2147 break;
2148 case 0x19: // MULTU
2149 {
2150 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2151 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
2152 signed char hi=get_reg(i_regs->regmap,HIREG);
2153 signed char lo=get_reg(i_regs->regmap,LOREG);
2154 assert(m1>=0);
2155 assert(m2>=0);
2156 assert(hi>=0);
2157 assert(lo>=0);
2158 emit_umull(m1,m2,hi,lo);
2159 }
2160 break;
2161 case 0x1A: // DIV
2162 {
2163 signed char d1=get_reg(i_regs->regmap,dops[i].rs1);
2164 signed char d2=get_reg(i_regs->regmap,dops[i].rs2);
2165 signed char quotient=get_reg(i_regs->regmap,LOREG);
2166 signed char remainder=get_reg(i_regs->regmap,HIREG);
2167 void *jaddr_div0;
2168 assert(d1>=0);
2169 assert(d2>=0);
2170 assert(quotient>=0);
2171 assert(remainder>=0);
2172 emit_movs(d1,remainder);
2173 emit_movimm(0xffffffff,quotient);
2174 emit_negmi(quotient,quotient); // .. quotient and ..
2175 emit_negmi(remainder,remainder); // .. remainder for div0 case (will be negated back after jump)
2176 emit_movs(d2,HOST_TEMPREG);
2177 jaddr_div0 = out;
2178 emit_jeq(0); // Division by zero
2179 emit_negsmi(HOST_TEMPREG,HOST_TEMPREG);
2180#ifdef HAVE_ARMV5
2181 emit_clz(HOST_TEMPREG,quotient);
2182 emit_shl(HOST_TEMPREG,quotient,HOST_TEMPREG); // shifted divisor
2183#else
2184 emit_movimm(0,quotient);
2185 emit_addpl_imm(quotient,1,quotient);
2186 emit_lslpls_imm(HOST_TEMPREG,1,HOST_TEMPREG);
2187 emit_jns(out-2*4);
2188#endif
2189 emit_orimm(quotient,1<<31,quotient);
2190 emit_shr(quotient,quotient,quotient);
2191 emit_cmp(remainder,HOST_TEMPREG);
2192 emit_subcs(remainder,HOST_TEMPREG,remainder);
2193 emit_adcs(quotient,quotient,quotient);
2194 emit_shrimm(HOST_TEMPREG,1,HOST_TEMPREG);
2195 emit_jcc(out-16); // -4
2196 emit_teq(d1,d2);
2197 emit_negmi(quotient,quotient);
2198 set_jump_target(jaddr_div0, out);
2199 emit_test(d1,d1);
2200 emit_negmi(remainder,remainder);
2201 }
2202 break;
2203 case 0x1B: // DIVU
2204 {
2205 signed char d1=get_reg(i_regs->regmap,dops[i].rs1); // dividend
2206 signed char d2=get_reg(i_regs->regmap,dops[i].rs2); // divisor
2207 signed char quotient=get_reg(i_regs->regmap,LOREG);
2208 signed char remainder=get_reg(i_regs->regmap,HIREG);
2209 void *jaddr_div0;
2210 assert(d1>=0);
2211 assert(d2>=0);
2212 assert(quotient>=0);
2213 assert(remainder>=0);
2214 emit_mov(d1,remainder);
2215 emit_movimm(0xffffffff,quotient); // div0 case
2216 emit_test(d2,d2);
2217 jaddr_div0 = out;
2218 emit_jeq(0); // Division by zero
2219#ifdef HAVE_ARMV5
2220 emit_clz(d2,HOST_TEMPREG);
2221 emit_movimm(1<<31,quotient);
2222 emit_shl(d2,HOST_TEMPREG,d2);
2223#else
2224 emit_movimm(0,HOST_TEMPREG);
2225 emit_addpl_imm(HOST_TEMPREG,1,HOST_TEMPREG);
2226 emit_lslpls_imm(d2,1,d2);
2227 emit_jns(out-2*4);
2228 emit_movimm(1<<31,quotient);
2229#endif
2230 emit_shr(quotient,HOST_TEMPREG,quotient);
2231 emit_cmp(remainder,d2);
2232 emit_subcs(remainder,d2,remainder);
2233 emit_adcs(quotient,quotient,quotient);
2234 emit_shrcc_imm(d2,1,d2);
2235 emit_jcc(out-16); // -4
2236 set_jump_target(jaddr_div0, out);
2237 }
2238 break;
2239 }
2240 }
2241 else
2242 {
2243 signed char hr=get_reg(i_regs->regmap,HIREG);
2244 signed char lr=get_reg(i_regs->regmap,LOREG);
2245 if ((dops[i].opcode2==0x1A || dops[i].opcode2==0x1B) && dops[i].rs2==0) // div 0
2246 {
2247 if (dops[i].rs1) {
2248 signed char numerator = get_reg(i_regs->regmap, dops[i].rs1);
2249 assert(numerator >= 0);
2250 if (hr < 0)
2251 hr = HOST_TEMPREG;
2252 emit_movs(numerator, hr);
2253 if (lr >= 0) {
2254 if (dops[i].opcode2 == 0x1A) { // DIV
2255 emit_movimm(0xffffffff, lr);
2256 emit_negmi(lr, lr);
2257 }
2258 else
2259 emit_movimm(~0, lr);
2260 }
2261 }
2262 else {
2263 if (hr >= 0) emit_zeroreg(hr);
2264 if (lr >= 0) emit_movimm(~0,lr);
2265 }
2266 }
2267 else if ((dops[i].opcode2==0x1A || dops[i].opcode2==0x1B) && dops[i].rs1==0)
2268 {
2269 signed char denominator = get_reg(i_regs->regmap, dops[i].rs2);
2270 assert(denominator >= 0);
2271 if (hr >= 0) emit_zeroreg(hr);
2272 if (lr >= 0) {
2273 emit_zeroreg(lr);
2274 emit_test(denominator, denominator);
2275 emit_mvneq(lr, lr);
2276 }
2277 }
2278 else
2279 {
2280 // Multiply by zero is zero.
2281 if (hr >= 0) emit_zeroreg(hr);
2282 if (lr >= 0) emit_zeroreg(lr);
2283 }
2284 }
2285}
2286#define multdiv_assemble multdiv_assemble_arm
2287
2288static void do_jump_vaddr(int rs)
2289{
2290 emit_far_jump(jump_vaddr_reg[rs]);
2291}
2292
2293static void do_preload_rhash(int r) {
2294 // Don't need this for ARM. On x86, this puts the value 0xf8 into the
2295 // register. On ARM the hash can be done with a single instruction (below)
2296}
2297
2298static void do_preload_rhtbl(int ht) {
2299 emit_addimm(FP,(int)&mini_ht-(int)&dynarec_local,ht);
2300}
2301
2302static void do_rhash(int rs,int rh) {
2303 emit_andimm(rs,0xf8,rh);
2304}
2305
2306static void do_miniht_load(int ht,int rh) {
2307 assem_debug("ldr %s,[%s,%s]!\n",regname[rh],regname[ht],regname[rh]);
2308 output_w32(0xe7b00000|rd_rn_rm(rh,ht,rh));
2309}
2310
2311static void do_miniht_jump(int rs,int rh,int ht) {
2312 emit_cmp(rh,rs);
2313 emit_ldreq_indexed(ht,4,15);
2314 #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
2315 if(rs!=7)
2316 emit_mov(rs,7);
2317 rs=7;
2318 #endif
2319 do_jump_vaddr(rs);
2320}
2321
2322static void do_miniht_insert(u_int return_address,int rt,int temp) {
2323 #ifndef HAVE_ARMV7
2324 emit_movimm(return_address,rt); // PC into link register
2325 add_to_linker(out,return_address,1);
2326 emit_pcreladdr(temp);
2327 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
2328 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
2329 #else
2330 emit_movw(return_address&0x0000FFFF,rt);
2331 add_to_linker(out,return_address,1);
2332 emit_pcreladdr(temp);
2333 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
2334 emit_movt(return_address&0xFFFF0000,rt);
2335 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
2336 #endif
2337}
2338
2339// CPU-architecture-specific initialization
2340static void arch_init(void)
2341{
2342 uintptr_t diff = (u_char *)&ndrc->tramp.f - (u_char *)&ndrc->tramp.ops - 8;
2343 struct tramp_insns *ops = ndrc->tramp.ops;
2344 size_t i;
2345 assert(!(diff & 3));
2346 assert(diff < 0x1000);
2347 start_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops));
2348 for (i = 0; i < ARRAY_SIZE(ndrc->tramp.ops); i++)
2349 ops[i].ldrpc = 0xe5900000 | rd_rn_rm(15,15,0) | diff; // ldr pc, [=val]
2350 end_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops));
2351}
2352
2353// vim:shiftwidth=2:expandtab