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