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