drc: update according to interpreter
[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 rs=get_reg(regmap,target);
1741 int rt=get_reg(regmap,target);
1742 if(rs<0) rs=get_reg_temp(regmap);
1743 assert(rs>=0);
1744 u_int is_dynamic;
1745 uintptr_t host_addr = 0;
1746 void *handler;
1747 int cc=get_reg(regmap,CCREG);
1748 if(pcsx_direct_read(type,addr,adj,cc,target?rs:-1,rt))
1749 return;
1750 handler = get_direct_memhandler(mem_rtab, addr, type, &host_addr);
1751 if (handler == NULL) {
1752 if(rt<0||dops[i].rt1==0)
1753 return;
1754 if(addr!=host_addr)
1755 emit_movimm_from(addr,rs,host_addr,rs);
1756 switch(type) {
1757 case LOADB_STUB: emit_movsbl_indexed(0,rs,rt); break;
1758 case LOADBU_STUB: emit_movzbl_indexed(0,rs,rt); break;
1759 case LOADH_STUB: emit_movswl_indexed(0,rs,rt); break;
1760 case LOADHU_STUB: emit_movzwl_indexed(0,rs,rt); break;
1761 case LOADW_STUB: emit_readword_indexed(0,rs,rt); break;
1762 default: assert(0);
1763 }
1764 return;
1765 }
1766 is_dynamic=pcsxmem_is_handler_dynamic(addr);
1767 if(is_dynamic) {
1768 if(type==LOADB_STUB||type==LOADBU_STUB)
1769 handler=jump_handler_read8;
1770 if(type==LOADH_STUB||type==LOADHU_STUB)
1771 handler=jump_handler_read16;
1772 if(type==LOADW_STUB)
1773 handler=jump_handler_read32;
1774 }
1775
1776 // call a memhandler
1777 if(rt>=0&&dops[i].rt1!=0)
1778 reglist&=~(1<<rt);
1779 save_regs(reglist);
1780 if(target==0)
1781 emit_movimm(addr,0);
1782 else if(rs!=0)
1783 emit_mov(rs,0);
1784 if(cc<0)
1785 emit_loadreg(CCREG,2);
1786 if(is_dynamic) {
1787 emit_movimm(((u_int *)mem_rtab)[addr>>12]<<1,1);
1788 emit_addimm(cc<0?2:cc,adj,2);
1789 }
1790 else {
1791 emit_readword(&last_count,3);
1792 emit_addimm(cc<0?2:cc,adj,2);
1793 emit_add(2,3,2);
1794 emit_writeword(2,&psxRegs.cycle);
1795 }
1796
1797 emit_far_call(handler);
1798
1799 if(rt>=0&&dops[i].rt1!=0) {
1800 switch(type) {
1801 case LOADB_STUB: emit_signextend8(0,rt); break;
1802 case LOADBU_STUB: emit_andimm(0,0xff,rt); break;
1803 case LOADH_STUB: emit_signextend16(0,rt); break;
1804 case LOADHU_STUB: emit_andimm(0,0xffff,rt); break;
1805 case LOADW_STUB: if(rt!=0) emit_mov(0,rt); break;
1806 default: assert(0);
1807 }
1808 }
1809 restore_regs(reglist);
1810}
1811
1812static void do_writestub(int n)
1813{
1814 assem_debug("do_writestub %x\n",start+stubs[n].a*4);
1815 literal_pool(256);
1816 set_jump_target(stubs[n].addr, out);
1817 enum stub_type type=stubs[n].type;
1818 int i=stubs[n].a;
1819 int rs=stubs[n].b;
1820 const struct regstat *i_regs=(struct regstat *)stubs[n].c;
1821 u_int reglist=stubs[n].e;
1822 const signed char *i_regmap=i_regs->regmap;
1823 int rt,r;
1824 if(dops[i].itype==C2LS) {
1825 rt=get_reg(i_regmap,r=FTEMP);
1826 }else{
1827 rt=get_reg(i_regmap,r=dops[i].rs2);
1828 }
1829 assert(rs>=0);
1830 assert(rt>=0);
1831 int rtmp,temp=-1,temp2=HOST_TEMPREG,regs_saved=0;
1832 void *restore_jump = NULL;
1833 int reglist2=reglist|(1<<rs)|(1<<rt);
1834 for(rtmp=0;rtmp<=12;rtmp++) {
1835 if(((1<<rtmp)&0x13ff)&&((1<<rtmp)&reglist2)==0) {
1836 temp=rtmp; break;
1837 }
1838 }
1839 if(temp==-1) {
1840 save_regs(reglist);
1841 regs_saved=1;
1842 for(rtmp=0;rtmp<=3;rtmp++)
1843 if(rtmp!=rs&&rtmp!=rt)
1844 {temp=rtmp;break;}
1845 }
1846 if((regs_saved||(reglist2&8)==0)&&temp!=3&&rs!=3&&rt!=3)
1847 temp2=3;
1848 emit_readword(&mem_wtab,temp);
1849 emit_shrimm(rs,12,temp2);
1850 emit_readword_dualindexedx4(temp,temp2,temp2);
1851 emit_lsls_imm(temp2,1,temp2);
1852 switch(type) {
1853 case STOREB_STUB: emit_strccb_dualindexed(temp2,rs,rt); break;
1854 case STOREH_STUB: emit_strcch_dualindexed(temp2,rs,rt); break;
1855 case STOREW_STUB: emit_strcc_dualindexed(temp2,rs,rt); break;
1856 default: assert(0);
1857 }
1858 if(regs_saved) {
1859 restore_jump=out;
1860 emit_jcc(0); // jump to reg restore
1861 }
1862 else
1863 emit_jcc(stubs[n].retaddr); // return address (invcode check)
1864
1865 if(!regs_saved)
1866 save_regs(reglist);
1867 void *handler=NULL;
1868 switch(type) {
1869 case STOREB_STUB: handler=jump_handler_write8; break;
1870 case STOREH_STUB: handler=jump_handler_write16; break;
1871 case STOREW_STUB: handler=jump_handler_write32; break;
1872 default: assert(0);
1873 }
1874 assert(handler);
1875 pass_args(rs,rt);
1876 if(temp2!=3)
1877 emit_mov(temp2,3);
1878 int cc=get_reg(i_regmap,CCREG);
1879 if(cc<0)
1880 emit_loadreg(CCREG,2);
1881 emit_addimm(cc<0?2:cc,(int)stubs[n].d,2);
1882 // returns new cycle_count
1883 emit_far_call(handler);
1884 emit_addimm(0,-(int)stubs[n].d,cc<0?2:cc);
1885 if(cc<0)
1886 emit_storereg(CCREG,2);
1887 if(restore_jump)
1888 set_jump_target(restore_jump, out);
1889 restore_regs(reglist);
1890 emit_jmp(stubs[n].retaddr);
1891}
1892
1893static void inline_writestub(enum stub_type type, int i, u_int addr,
1894 const signed char regmap[], int target, int adj, u_int reglist)
1895{
1896 int rs=get_reg_temp(regmap);
1897 int rt=get_reg(regmap,target);
1898 assert(rs>=0);
1899 assert(rt>=0);
1900 uintptr_t host_addr = 0;
1901 void *handler = get_direct_memhandler(mem_wtab, addr, type, &host_addr);
1902 if (handler == NULL) {
1903 if(addr!=host_addr)
1904 emit_movimm_from(addr,rs,host_addr,rs);
1905 switch(type) {
1906 case STOREB_STUB: emit_writebyte_indexed(rt,0,rs); break;
1907 case STOREH_STUB: emit_writehword_indexed(rt,0,rs); break;
1908 case STOREW_STUB: emit_writeword_indexed(rt,0,rs); break;
1909 default: assert(0);
1910 }
1911 return;
1912 }
1913
1914 // call a memhandler
1915 save_regs(reglist);
1916 pass_args(rs,rt);
1917 int cc=get_reg(regmap,CCREG);
1918 if(cc<0)
1919 emit_loadreg(CCREG,2);
1920 emit_addimm(cc<0?2:cc,adj,2);
1921 emit_movimm((u_int)handler,3);
1922 // returns new cycle_count
1923 emit_far_call(jump_handler_write_h);
1924 emit_addimm(0,-adj,cc<0?2:cc);
1925 if(cc<0)
1926 emit_storereg(CCREG,2);
1927 restore_regs(reglist);
1928}
1929
1930/* Special assem */
1931
1932static void c2op_prologue(u_int op, int i, const struct regstat *i_regs, u_int reglist)
1933{
1934 save_regs_all(reglist);
1935 cop2_do_stall_check(op, i, i_regs, 0);
1936#ifdef PCNT
1937 emit_movimm(op, 0);
1938 emit_far_call(pcnt_gte_start);
1939#endif
1940 emit_addimm(FP, (u_char *)&psxRegs.CP2D.r[0] - (u_char *)&dynarec_local, 0); // cop2 regs
1941}
1942
1943static void c2op_epilogue(u_int op,u_int reglist)
1944{
1945#ifdef PCNT
1946 emit_movimm(op,0);
1947 emit_far_call(pcnt_gte_end);
1948#endif
1949 restore_regs_all(reglist);
1950}
1951
1952static void c2op_call_MACtoIR(int lm,int need_flags)
1953{
1954 if(need_flags)
1955 emit_far_call(lm?gteMACtoIR_lm1:gteMACtoIR_lm0);
1956 else
1957 emit_far_call(lm?gteMACtoIR_lm1_nf:gteMACtoIR_lm0_nf);
1958}
1959
1960static void c2op_call_rgb_func(void *func,int lm,int need_ir,int need_flags)
1961{
1962 emit_far_call(func);
1963 // func is C code and trashes r0
1964 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
1965 if(need_flags||need_ir)
1966 c2op_call_MACtoIR(lm,need_flags);
1967 emit_far_call(need_flags?gteMACtoRGB:gteMACtoRGB_nf);
1968}
1969
1970static void c2op_assemble(int i, const struct regstat *i_regs)
1971{
1972 u_int c2op = source[i] & 0x3f;
1973 u_int reglist_full = get_host_reglist(i_regs->regmap);
1974 u_int reglist = reglist_full & CALLER_SAVE_REGS;
1975 int need_flags, need_ir;
1976
1977 if (gte_handlers[c2op]!=NULL) {
1978 need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
1979 need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
1980 assem_debug("gte op %08x, unneeded %016llx, need_flags %d, need_ir %d\n",
1981 source[i],gte_unneeded[i+1],need_flags,need_ir);
1982 if(HACK_ENABLED(NDHACK_GTE_NO_FLAGS))
1983 need_flags=0;
1984 int shift = (source[i] >> 19) & 1;
1985 int lm = (source[i] >> 10) & 1;
1986 switch(c2op) {
1987#ifndef DRC_DBG
1988 case GTE_MVMVA: {
1989#ifdef HAVE_ARMV5
1990 int v = (source[i] >> 15) & 3;
1991 int cv = (source[i] >> 13) & 3;
1992 int mx = (source[i] >> 17) & 3;
1993 reglist=reglist_full&(CALLER_SAVE_REGS|0xf0); // +{r4-r7}
1994 c2op_prologue(c2op,i,i_regs,reglist);
1995 /* r4,r5 = VXYZ(v) packed; r6 = &MX11(mx); r7 = &CV1(cv) */
1996 if(v<3)
1997 emit_ldrd(v*8,0,4);
1998 else {
1999 emit_movzwl_indexed(9*4,0,4); // gteIR
2000 emit_movzwl_indexed(10*4,0,6);
2001 emit_movzwl_indexed(11*4,0,5);
2002 emit_orrshl_imm(6,16,4);
2003 }
2004 if(mx<3)
2005 emit_addimm(0,32*4+mx*8*4,6);
2006 else
2007 emit_readword(&zeromem_ptr,6);
2008 if(cv<3)
2009 emit_addimm(0,32*4+(cv*8+5)*4,7);
2010 else
2011 emit_readword(&zeromem_ptr,7);
2012#ifdef __ARM_NEON__
2013 emit_movimm(source[i],1); // opcode
2014 emit_far_call(gteMVMVA_part_neon);
2015 if(need_flags) {
2016 emit_movimm(lm,1);
2017 emit_far_call(gteMACtoIR_flags_neon);
2018 }
2019#else
2020 if(cv==3&&shift)
2021 emit_far_call(gteMVMVA_part_cv3sh12_arm);
2022 else {
2023 emit_movimm(shift,1);
2024 emit_far_call(need_flags?gteMVMVA_part_arm:gteMVMVA_part_nf_arm);
2025 }
2026 if(need_flags||need_ir)
2027 c2op_call_MACtoIR(lm,need_flags);
2028#endif
2029#else /* if not HAVE_ARMV5 */
2030 c2op_prologue(c2op,i,i_regs,reglist);
2031 emit_movimm(source[i],1); // opcode
2032 emit_writeword(1,&psxRegs.code);
2033 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
2034#endif
2035 break;
2036 }
2037 case GTE_OP:
2038 c2op_prologue(c2op,i,i_regs,reglist);
2039 emit_far_call(shift?gteOP_part_shift:gteOP_part_noshift);
2040 if(need_flags||need_ir) {
2041 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
2042 c2op_call_MACtoIR(lm,need_flags);
2043 }
2044 break;
2045 case GTE_DPCS:
2046 c2op_prologue(c2op,i,i_regs,reglist);
2047 c2op_call_rgb_func(shift?gteDPCS_part_shift:gteDPCS_part_noshift,lm,need_ir,need_flags);
2048 break;
2049 case GTE_INTPL:
2050 c2op_prologue(c2op,i,i_regs,reglist);
2051 c2op_call_rgb_func(shift?gteINTPL_part_shift:gteINTPL_part_noshift,lm,need_ir,need_flags);
2052 break;
2053 case GTE_SQR:
2054 c2op_prologue(c2op,i,i_regs,reglist);
2055 emit_far_call(shift?gteSQR_part_shift:gteSQR_part_noshift);
2056 if(need_flags||need_ir) {
2057 emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
2058 c2op_call_MACtoIR(lm,need_flags);
2059 }
2060 break;
2061 case GTE_DCPL:
2062 c2op_prologue(c2op,i,i_regs,reglist);
2063 c2op_call_rgb_func(gteDCPL_part,lm,need_ir,need_flags);
2064 break;
2065 case GTE_GPF:
2066 c2op_prologue(c2op,i,i_regs,reglist);
2067 c2op_call_rgb_func(shift?gteGPF_part_shift:gteGPF_part_noshift,lm,need_ir,need_flags);
2068 break;
2069 case GTE_GPL:
2070 c2op_prologue(c2op,i,i_regs,reglist);
2071 c2op_call_rgb_func(shift?gteGPL_part_shift:gteGPL_part_noshift,lm,need_ir,need_flags);
2072 break;
2073#endif
2074 default:
2075 c2op_prologue(c2op,i,i_regs,reglist);
2076#ifdef DRC_DBG
2077 emit_movimm(source[i],1); // opcode
2078 emit_writeword(1,&psxRegs.code);
2079#endif
2080 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
2081 break;
2082 }
2083 c2op_epilogue(c2op,reglist);
2084 }
2085}
2086
2087static void c2op_ctc2_31_assemble(signed char sl, signed char temp)
2088{
2089 //value = value & 0x7ffff000;
2090 //if (value & 0x7f87e000) value |= 0x80000000;
2091 emit_shrimm(sl,12,temp);
2092 emit_shlimm(temp,12,temp);
2093 emit_testimm(temp,0x7f000000);
2094 emit_testeqimm(temp,0x00870000);
2095 emit_testeqimm(temp,0x0000e000);
2096 emit_orrne_imm(temp,0x80000000,temp);
2097}
2098
2099static void do_mfc2_31_one(u_int copr,signed char temp)
2100{
2101 emit_readword(&reg_cop2d[copr],temp);
2102 emit_lsls_imm(temp,16,temp);
2103 emit_cmovs_imm(0,temp);
2104 emit_cmpimm(temp,0xf80<<16);
2105 emit_andimm(temp,0xf80<<16,temp);
2106 emit_cmovae_imm(0xf80<<16,temp);
2107}
2108
2109static void c2op_mfc2_29_assemble(signed char tl, signed char temp)
2110{
2111 if (temp < 0) {
2112 host_tempreg_acquire();
2113 temp = HOST_TEMPREG;
2114 }
2115 do_mfc2_31_one(9,temp);
2116 emit_shrimm(temp,7+16,tl);
2117 do_mfc2_31_one(10,temp);
2118 emit_orrshr_imm(temp,2+16,tl);
2119 do_mfc2_31_one(11,temp);
2120 emit_orrshr_imm(temp,-3+16,tl);
2121 emit_writeword(tl,&reg_cop2d[29]);
2122 if (temp == HOST_TEMPREG)
2123 host_tempreg_release();
2124}
2125
2126static void multdiv_assemble_arm(int i, const struct regstat *i_regs)
2127{
2128 // case 0x18: MULT
2129 // case 0x19: MULTU
2130 // case 0x1A: DIV
2131 // case 0x1B: DIVU
2132 if(dops[i].rs1&&dops[i].rs2)
2133 {
2134 switch (dops[i].opcode2)
2135 {
2136 case 0x18: // MULT
2137 {
2138 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2139 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
2140 signed char hi=get_reg(i_regs->regmap,HIREG);
2141 signed char lo=get_reg(i_regs->regmap,LOREG);
2142 assert(m1>=0);
2143 assert(m2>=0);
2144 assert(hi>=0);
2145 assert(lo>=0);
2146 emit_smull(m1,m2,hi,lo);
2147 }
2148 break;
2149 case 0x19: // MULTU
2150 {
2151 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2152 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
2153 signed char hi=get_reg(i_regs->regmap,HIREG);
2154 signed char lo=get_reg(i_regs->regmap,LOREG);
2155 assert(m1>=0);
2156 assert(m2>=0);
2157 assert(hi>=0);
2158 assert(lo>=0);
2159 emit_umull(m1,m2,hi,lo);
2160 }
2161 break;
2162 case 0x1A: // DIV
2163 {
2164 signed char d1=get_reg(i_regs->regmap,dops[i].rs1);
2165 signed char d2=get_reg(i_regs->regmap,dops[i].rs2);
2166 signed char quotient=get_reg(i_regs->regmap,LOREG);
2167 signed char remainder=get_reg(i_regs->regmap,HIREG);
2168 void *jaddr_div0;
2169 assert(d1>=0);
2170 assert(d2>=0);
2171 assert(quotient>=0);
2172 assert(remainder>=0);
2173 emit_movs(d1,remainder);
2174 emit_movimm(0xffffffff,quotient);
2175 emit_negmi(quotient,quotient); // .. quotient and ..
2176 emit_negmi(remainder,remainder); // .. remainder for div0 case (will be negated back after jump)
2177 emit_movs(d2,HOST_TEMPREG);
2178 jaddr_div0 = out;
2179 emit_jeq(0); // Division by zero
2180 emit_negsmi(HOST_TEMPREG,HOST_TEMPREG);
2181#ifdef HAVE_ARMV5
2182 emit_clz(HOST_TEMPREG,quotient);
2183 emit_shl(HOST_TEMPREG,quotient,HOST_TEMPREG); // shifted divisor
2184#else
2185 emit_movimm(0,quotient);
2186 emit_addpl_imm(quotient,1,quotient);
2187 emit_lslpls_imm(HOST_TEMPREG,1,HOST_TEMPREG);
2188 emit_jns(out-2*4);
2189#endif
2190 emit_orimm(quotient,1<<31,quotient);
2191 emit_shr(quotient,quotient,quotient);
2192 emit_cmp(remainder,HOST_TEMPREG);
2193 emit_subcs(remainder,HOST_TEMPREG,remainder);
2194 emit_adcs(quotient,quotient,quotient);
2195 emit_shrimm(HOST_TEMPREG,1,HOST_TEMPREG);
2196 emit_jcc(out-16); // -4
2197 emit_teq(d1,d2);
2198 emit_negmi(quotient,quotient);
2199 set_jump_target(jaddr_div0, out);
2200 emit_test(d1,d1);
2201 emit_negmi(remainder,remainder);
2202 }
2203 break;
2204 case 0x1B: // DIVU
2205 {
2206 signed char d1=get_reg(i_regs->regmap,dops[i].rs1); // dividend
2207 signed char d2=get_reg(i_regs->regmap,dops[i].rs2); // divisor
2208 signed char quotient=get_reg(i_regs->regmap,LOREG);
2209 signed char remainder=get_reg(i_regs->regmap,HIREG);
2210 void *jaddr_div0;
2211 assert(d1>=0);
2212 assert(d2>=0);
2213 assert(quotient>=0);
2214 assert(remainder>=0);
2215 emit_mov(d1,remainder);
2216 emit_movimm(0xffffffff,quotient); // div0 case
2217 emit_test(d2,d2);
2218 jaddr_div0 = out;
2219 emit_jeq(0); // Division by zero
2220#ifdef HAVE_ARMV5
2221 emit_clz(d2,HOST_TEMPREG);
2222 emit_movimm(1<<31,quotient);
2223 emit_shl(d2,HOST_TEMPREG,d2);
2224#else
2225 emit_movimm(0,HOST_TEMPREG);
2226 emit_addpl_imm(HOST_TEMPREG,1,HOST_TEMPREG);
2227 emit_lslpls_imm(d2,1,d2);
2228 emit_jns(out-2*4);
2229 emit_movimm(1<<31,quotient);
2230#endif
2231 emit_shr(quotient,HOST_TEMPREG,quotient);
2232 emit_cmp(remainder,d2);
2233 emit_subcs(remainder,d2,remainder);
2234 emit_adcs(quotient,quotient,quotient);
2235 emit_shrcc_imm(d2,1,d2);
2236 emit_jcc(out-16); // -4
2237 set_jump_target(jaddr_div0, out);
2238 }
2239 break;
2240 }
2241 }
2242 else
2243 {
2244 signed char hr=get_reg(i_regs->regmap,HIREG);
2245 signed char lr=get_reg(i_regs->regmap,LOREG);
2246 if ((dops[i].opcode2==0x1A || dops[i].opcode2==0x1B) && dops[i].rs2==0) // div 0
2247 {
2248 if (dops[i].rs1) {
2249 signed char numerator = get_reg(i_regs->regmap, dops[i].rs1);
2250 assert(numerator >= 0);
2251 if (hr < 0)
2252 hr = HOST_TEMPREG;
2253 emit_movs(numerator, hr);
2254 if (lr >= 0) {
2255 if (dops[i].opcode2 == 0x1A) { // DIV
2256 emit_movimm(0xffffffff, lr);
2257 emit_negmi(lr, lr);
2258 }
2259 else
2260 emit_movimm(~0, lr);
2261 }
2262 }
2263 else {
2264 if (hr >= 0) emit_zeroreg(hr);
2265 if (lr >= 0) emit_movimm(~0,lr);
2266 }
2267 }
2268 else if ((dops[i].opcode2==0x1A || dops[i].opcode2==0x1B) && dops[i].rs1==0)
2269 {
2270 signed char denominator = get_reg(i_regs->regmap, dops[i].rs2);
2271 assert(denominator >= 0);
2272 if (hr >= 0) emit_zeroreg(hr);
2273 if (lr >= 0) {
2274 emit_zeroreg(lr);
2275 emit_test(denominator, denominator);
2276 emit_mvneq(lr, lr);
2277 }
2278 }
2279 else
2280 {
2281 // Multiply by zero is zero.
2282 if (hr >= 0) emit_zeroreg(hr);
2283 if (lr >= 0) emit_zeroreg(lr);
2284 }
2285 }
2286}
2287#define multdiv_assemble multdiv_assemble_arm
2288
2289static void do_jump_vaddr(int rs)
2290{
2291 emit_far_jump(jump_vaddr_reg[rs]);
2292}
2293
2294static void do_preload_rhash(int r) {
2295 // Don't need this for ARM. On x86, this puts the value 0xf8 into the
2296 // register. On ARM the hash can be done with a single instruction (below)
2297}
2298
2299static void do_preload_rhtbl(int ht) {
2300 emit_addimm(FP,(int)&mini_ht-(int)&dynarec_local,ht);
2301}
2302
2303static void do_rhash(int rs,int rh) {
2304 emit_andimm(rs,0xf8,rh);
2305}
2306
2307static void do_miniht_load(int ht,int rh) {
2308 assem_debug("ldr %s,[%s,%s]!\n",regname[rh],regname[ht],regname[rh]);
2309 output_w32(0xe7b00000|rd_rn_rm(rh,ht,rh));
2310}
2311
2312static void do_miniht_jump(int rs,int rh,int ht) {
2313 emit_cmp(rh,rs);
2314 emit_ldreq_indexed(ht,4,15);
2315 #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
2316 if(rs!=7)
2317 emit_mov(rs,7);
2318 rs=7;
2319 #endif
2320 do_jump_vaddr(rs);
2321}
2322
2323static void do_miniht_insert(u_int return_address,int rt,int temp) {
2324 #ifndef HAVE_ARMV7
2325 emit_movimm(return_address,rt); // PC into link register
2326 add_to_linker(out,return_address,1);
2327 emit_pcreladdr(temp);
2328 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
2329 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
2330 #else
2331 emit_movw(return_address&0x0000FFFF,rt);
2332 add_to_linker(out,return_address,1);
2333 emit_pcreladdr(temp);
2334 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
2335 emit_movt(return_address&0xFFFF0000,rt);
2336 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
2337 #endif
2338}
2339
2340// CPU-architecture-specific initialization
2341static void arch_init(void)
2342{
2343 uintptr_t diff = (u_char *)&ndrc->tramp.f - (u_char *)&ndrc->tramp.ops - 8;
2344 struct tramp_insns *ops = ndrc->tramp.ops;
2345 size_t i;
2346 assert(!(diff & 3));
2347 assert(diff < 0x1000);
2348 start_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops));
2349 for (i = 0; i < ARRAY_SIZE(ndrc->tramp.ops); i++)
2350 ops[i].ldrpc = 0xe5900000 | rd_rn_rm(15,15,0) | diff; // ldr pc, [=val]
2351 end_tcache_write(ops, (u_char *)ops + sizeof(ndrc->tramp.ops));
2352}
2353
2354// vim:shiftwidth=2:expandtab