drc: update according to the interpreter (3)
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / assem_arm.c
CommitLineData
57871462 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
c6c3b1b3 2 * Mupen64plus/PCSX - assem_arm.c *
20d507ba 3 * Copyright (C) 2009-2011 Ari64 *
2a014d73 4 * Copyright (C) 2010-2021 GraÅžvydas "notaz" Ignotas *
57871462 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
6c0eefaf 22#define FLAGLESS
23#include "../gte.h"
24#undef FLAGLESS
054175e9 25#include "../gte_arm.h"
26#include "../gte_neon.h"
27#include "pcnt.h"
665f33e1 28#include "arm_features.h"
054175e9 29
dd114d7d 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
57871462 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
b14b6a8f 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,
57871462 64 0,
b14b6a8f 65 jump_vaddr_r12,
57871462 66 0,
67 0,
b14b6a8f 68 0
69};
57871462 70
0bbd1454 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
57871462 102/* Linker */
103
df4dc2b1 104static void set_jump_target(void *addr, void *target_)
57871462 105{
df4dc2b1 106 u_int target = (u_int)target_;
107 u_char *ptr = addr;
57871462 108 u_int *ptr2=(u_int *)ptr;
109 if(ptr[3]==0xe2) {
110 assert((target-(u_int)ptr2-8)<1024);
df4dc2b1 111 assert(((uintptr_t)addr&3)==0);
57871462 112 assert((target&3)==0);
113 *ptr2=(*ptr2&0xFFFFF000)|((target-(u_int)ptr2-8)>>2)|0xF00;
df4dc2b1 114 //printf("target=%x addr=%p insn=%x\n",target,addr,*ptr2);
57871462 115 }
116 else if(ptr[3]==0x72) {
117 // generated by emit_jno_unlikely
118 if((target-(u_int)ptr2-8)<1024) {
df4dc2b1 119 assert(((uintptr_t)addr&3)==0);
57871462 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)) {
df4dc2b1 124 assert(((uintptr_t)addr&3)==0);
57871462 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.
e2b5e7aa 139#if 0
140static void set_jump_target_fillslot(int addr,u_int target,int copy)
57871462 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}
e2b5e7aa 169#endif
57871462 170
171/* Literal pool */
e2b5e7aa 172static void add_literal(int addr,int val)
57871462 173{
15776b68 174 assert(literalcount<sizeof(literals)/sizeof(literals[0]));
57871462 175 literals[literalcount][0]=addr;
176 literals[literalcount][1]=val;
9f51b4b9 177 literalcount++;
178}
57871462 179
d148d265 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)
57871462 183{
184 int *ptr=(int *)(stub+4);
d148d265 185 assert((*ptr&0x0fff0000)==0x059f0000); // ldr rx, [pc, #ofs]
57871462 186 u_int offset=*ptr&0xfff;
d148d265 187 void **l_ptr=(void *)ptr+offset+8;
188 return *l_ptr;
57871462 189}
190
f968d35d 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
104df9d3 195#if 0
643aeae3 196static void *get_pointer(void *stub)
57871462 197{
198 //printf("get_pointer(%x)\n",(int)stub);
d148d265 199 int *i_ptr=find_extjump_insn(stub);
3d680478 200 assert((*i_ptr&0x0f000000)==0x0a000000); // b
643aeae3 201 return (u_char *)i_ptr+((*i_ptr<<8)>>6)+8;
57871462 202}
104df9d3 203#endif
57871462 204
57871462 205// Allocate a specific ARM register.
e2b5e7aa 206static void alloc_arm_reg(struct regstat *cur,int i,signed char reg,int hr)
57871462 207{
208 int n;
f776eb14 209 int dirty=0;
9f51b4b9 210
57871462 211 // see if it's already allocated (and dealloc it)
212 for(n=0;n<HOST_REGS;n++)
213 {
f776eb14 214 if(n!=EXCLUDE_REG&&cur->regmap[n]==reg) {
215 dirty=(cur->dirty>>n)&1;
216 cur->regmap[n]=-1;
217 }
57871462 218 }
9f51b4b9 219
57871462 220 cur->regmap[hr]=reg;
221 cur->dirty&=~(1<<hr);
f776eb14 222 cur->dirty|=dirty<<hr;
57871462 223 cur->isconst&=~(1<<hr);
224}
225
226// Alloc cycle count into dedicated register
e2b5e7aa 227static void alloc_cc(struct regstat *cur,int i)
57871462 228{
229 alloc_arm_reg(cur,i,CCREG,HOST_CCREG);
230}
231
57871462 232/* Assembler */
233
e2b5e7aa 234static unused char regname[16][4] = {
57871462 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
e2b5e7aa 252static void output_w32(u_int word)
57871462 253{
254 *((u_int *)out)=word;
255 out+=4;
256}
e2b5e7aa 257
258static u_int rd_rn_rm(u_int rd, u_int rn, u_int rm)
57871462 259{
260 assert(rd<16);
261 assert(rn<16);
262 assert(rm<16);
263 return((rn<<16)|(rd<<12)|rm);
264}
e2b5e7aa 265
266static u_int rd_rn_imm_shift(u_int rd, u_int rn, u_int imm, u_int shift)
57871462 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}
e2b5e7aa 274
275static u_int genimm(u_int imm,u_int *encoded)
57871462 276{
c2e3bd42 277 *encoded=0;
278 if(imm==0) return 1;
57871462 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}
e2b5e7aa 290
291static void genimm_checked(u_int imm,u_int *encoded)
cfbd3c6e 292{
293 u_int ret=genimm(imm,encoded);
294 assert(ret);
581335b0 295 (void)ret;
cfbd3c6e 296}
e2b5e7aa 297
298static u_int genjmp(u_int addr)
57871462 299{
7c3a5182 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();
e80343e2 305 return 0;
306 }
57871462 307 return ((u_int)offset>>2)&0xffffff;
308}
309
d1e4ebd9 310static unused void emit_breakpoint(void)
311{
312 assem_debug("bkpt #0\n");
313 //output_w32(0xe1200070);
314 output_w32(0xe7f001f0);
315}
316
e2b5e7aa 317static void emit_mov(int rs,int rt)
57871462 318{
319 assem_debug("mov %s,%s\n",regname[rt],regname[rs]);
320 output_w32(0xe1a00000|rd_rn_rm(rt,0,rs));
321}
322
e2b5e7aa 323static void emit_movs(int rs,int rt)
57871462 324{
325 assem_debug("movs %s,%s\n",regname[rt],regname[rs]);
326 output_w32(0xe1b00000|rd_rn_rm(rt,0,rs));
327}
328
e2b5e7aa 329static void emit_add(int rs1,int rs2,int rt)
57871462 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
39b71d9a 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
e2b5e7aa 342static void emit_adcs(int rs1,int rs2,int rt)
57871462 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
e2b5e7aa 348static void emit_neg(int rs, int rt)
57871462 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
a5cd72d0 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
e2b5e7aa 360static void emit_sub(int rs1,int rs2,int rt)
57871462 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
a5cd72d0 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
e2b5e7aa 372static void emit_zeroreg(int rt)
57871462 373{
374 assem_debug("mov %s,#0\n",regname[rt]);
375 output_w32(0xe3a00000|rd_rn_rm(rt,0,0));
376}
377
e2b5e7aa 378static void emit_loadlp(u_int imm,u_int rt)
790ee18e 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}
e2b5e7aa 384
33788798 385#ifdef HAVE_ARMV7
e2b5e7aa 386static void emit_movw(u_int imm,u_int rt)
790ee18e 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}
e2b5e7aa 392
393static void emit_movt(u_int imm,u_int rt)
790ee18e 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}
33788798 398#endif
e2b5e7aa 399
400static void emit_movimm(u_int imm,u_int rt)
790ee18e 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) {
665f33e1 410 #ifndef HAVE_ARMV7
790ee18e 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{
665f33e1 419 #ifndef HAVE_ARMV7
790ee18e 420 emit_loadlp(imm,rt);
421 #else
422 emit_movw(imm&0x0000FFFF,rt);
423 emit_movt(imm&0xFFFF0000,rt);
424 #endif
425 }
426}
e2b5e7aa 427
428static void emit_pcreladdr(u_int rt)
790ee18e 429{
430 assem_debug("add %s,pc,#?\n",regname[rt]);
431 output_w32(0xe2800000|rd_rn_rm(rt,15,0));
432}
433
e2b5e7aa 434static void emit_loadreg(int r, int hr)
57871462 435{
53358c1d 436 assert(hr != EXCLUDE_REG);
437 if (r == 0)
57871462 438 emit_zeroreg(hr);
439 else {
33788798 440 void *addr;
7c3a5182 441 switch (r) {
442 //case HIREG: addr = &hi; break;
443 //case LOREG: addr = &lo; break;
33788798 444 case CCREG: addr = &cycle_count; break;
33788798 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;
7c3a5182 451 }
33788798 452 u_int offset = (u_char *)addr - (u_char *)&dynarec_local;
57871462 453 assert(offset<4096);
6cc8d23c 454 assem_debug("ldr %s,fp+%d # r%d\n",regname[hr],offset,r);
57871462 455 output_w32(0xe5900000|rd_rn_rm(hr,FP,0)|offset);
456 }
457}
e2b5e7aa 458
459static void emit_storereg(int r, int hr)
57871462 460{
53358c1d 461 assert(hr != EXCLUDE_REG);
7c3a5182 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 }
57871462 469 u_int offset = addr-(u_int)&dynarec_local;
470 assert(offset<4096);
6cc8d23c 471 assem_debug("str %s,fp+%d # r%d\n",regname[hr],offset,r);
57871462 472 output_w32(0xe5800000|rd_rn_rm(hr,FP,0)|offset);
473}
474
e2b5e7aa 475static void emit_test(int rs, int rt)
57871462 476{
477 assem_debug("tst %s,%s\n",regname[rs],regname[rt]);
478 output_w32(0xe1100000|rd_rn_rm(0,rs,rt));
479}
480
e2b5e7aa 481static void emit_testimm(int rs,int imm)
57871462 482{
483 u_int armval;
5a05d80c 484 assem_debug("tst %s,#%d\n",regname[rs],imm);
cfbd3c6e 485 genimm_checked(imm,&armval);
57871462 486 output_w32(0xe3100000|rd_rn_rm(0,rs,0)|armval);
487}
488
e2b5e7aa 489static void emit_testeqimm(int rs,int imm)
b9b61529 490{
491 u_int armval;
492 assem_debug("tsteq %s,$%d\n",regname[rs],imm);
cfbd3c6e 493 genimm_checked(imm,&armval);
b9b61529 494 output_w32(0x03100000|rd_rn_rm(0,rs,0)|armval);
495}
496
e2b5e7aa 497static void emit_not(int rs,int rt)
57871462 498{
499 assem_debug("mvn %s,%s\n",regname[rt],regname[rs]);
500 output_w32(0xe1e00000|rd_rn_rm(rt,0,rs));
501}
502
a5cd72d0 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
e2b5e7aa 509static void emit_and(u_int rs1,u_int rs2,u_int rt)
57871462 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
e2b5e7aa 515static void emit_or(u_int rs1,u_int rs2,u_int rt)
57871462 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}
e2b5e7aa 520
e2b5e7aa 521static void emit_orrshl_imm(u_int rs,u_int imm,u_int rt)
f70d384d 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
e2b5e7aa 530static void emit_orrshr_imm(u_int rs,u_int imm,u_int rt)
576bbd8f 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
e2b5e7aa 539static void emit_xor(u_int rs1,u_int rs2,u_int rt)
57871462 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
3968e69e 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
e2b5e7aa 551static void emit_addimm(u_int rs,int imm,u_int rt)
57871462 552{
553 assert(rs<16);
554 assert(rt<16);
555 if(imm!=0) {
57871462 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)) {
8a0a8423 561 assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-imm);
57871462 562 output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
397614d0 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) {
57871462 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));
397614d0 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);
57871462 587 }
588 }
589 else if(rs!=rt) emit_mov(rs,rt);
590}
591
bc7c5acb 592static void emit_addimm_ptr(u_int rs, uintptr_t imm, u_int rt)
593{
594 emit_addimm(rs, imm, rt);
595}
596
a5cd72d0 597static void emit_addimm_and_set_flags3(u_int rs, int imm, u_int rt)
57871462 598{
599 assert(imm>-65536&&imm<65536);
600 u_int armval;
a5cd72d0 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);
57871462 612 assem_debug("subs %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF);
a5cd72d0 613 output_w32(0xe2400000|rd_rn_imm_shift(rt,rs,(-imm)>>8,8));
57871462 614 output_w32(0xe2500000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0));
a5cd72d0 615 } else {
616 assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00);
57871462 617 assem_debug("adds %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF);
a5cd72d0 618 output_w32(0xe2800000|rd_rn_imm_shift(rt,rs,imm>>8,8));
57871462 619 output_w32(0xe2900000|rd_rn_imm_shift(rt,rt,imm&0xff,0));
620 }
621}
e2b5e7aa 622
a5cd72d0 623static void emit_addimm_and_set_flags(int imm, u_int rt)
624{
625 emit_addimm_and_set_flags3(rt, imm, rt);
626}
627
e2b5e7aa 628static void emit_addnop(u_int r)
57871462 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
e2b5e7aa 635static void emit_andimm(int rs,int imm,int rt)
57871462 636{
637 u_int armval;
790ee18e 638 if(imm==0) {
639 emit_zeroreg(rt);
640 }else if(genimm(imm,&armval)) {
57871462 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) {
332a4533 647 #ifndef HAVE_ARMV6
57871462 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);
665f33e1 658 #ifndef HAVE_ARMV7
57871462 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
e2b5e7aa 671static void emit_orimm(int rs,int imm,int rt)
57871462 672{
673 u_int armval;
790ee18e 674 if(imm==0) {
675 if(rs!=rt) emit_mov(rs,rt);
676 }else if(genimm(imm,&armval)) {
57871462 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
e2b5e7aa 688static void emit_xorimm(int rs,int imm,int rt)
57871462 689{
57871462 690 u_int armval;
790ee18e 691 if(imm==0) {
692 if(rs!=rt) emit_mov(rs,rt);
693 }else if(genimm(imm,&armval)) {
57871462 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{
514ed0d9 697 assert(imm>0&&imm<65536);
57871462 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
e2b5e7aa 705static void emit_shlimm(int rs,u_int imm,int rt)
57871462 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
e2b5e7aa 714static void emit_lsls_imm(int rs,int imm,int rt)
c6c3b1b3 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
e2b5e7aa 722static unused void emit_lslpls_imm(int rs,int imm,int rt)
665f33e1 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
e2b5e7aa 730static void emit_shrimm(int rs,u_int imm,int rt)
57871462 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
e2b5e7aa 738static void emit_sarimm(int rs,u_int imm,int rt)
57871462 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
e2b5e7aa 746static void emit_rorimm(int rs,u_int imm,int rt)
57871462 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
e2b5e7aa 754static void emit_signextend16(int rs,int rt)
b9b61529 755{
332a4533 756 #ifndef HAVE_ARMV6
b9b61529 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
e2b5e7aa 765static void emit_signextend8(int rs,int rt)
c6c3b1b3 766{
332a4533 767 #ifndef HAVE_ARMV6
c6c3b1b3 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
e2b5e7aa 776static void emit_shl(u_int rs,u_int shift,u_int rt)
57871462 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}
e2b5e7aa 785
786static void emit_shr(u_int rs,u_int shift,u_int rt)
57871462 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}
e2b5e7aa 794
795static void emit_sar(u_int rs,u_int shift,u_int rt)
57871462 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}
57871462 803
3968e69e 804static unused void emit_orrshl(u_int rs,u_int shift,u_int rt)
57871462 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}
e2b5e7aa 812
3968e69e 813static unused void emit_orrshr(u_int rs,u_int shift,u_int rt)
57871462 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
e2b5e7aa 822static void emit_cmpimm(int rs,int imm)
57871462 823{
824 u_int armval;
825 if(genimm(imm,&armval)) {
5a05d80c 826 assem_debug("cmp %s,#%d\n",regname[rs],imm);
57871462 827 output_w32(0xe3500000|rd_rn_rm(0,rs,0)|armval);
828 }else if(genimm(-imm,&armval)) {
5a05d80c 829 assem_debug("cmn %s,#%d\n",regname[rs],imm);
57871462 830 output_w32(0xe3700000|rd_rn_rm(0,rs,0)|armval);
831 }else if(imm>0) {
832 assert(imm<65536);
57871462 833 emit_movimm(imm,HOST_TEMPREG);
57871462 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);
57871462 838 emit_movimm(-imm,HOST_TEMPREG);
57871462 839 assem_debug("cmn %s,r14\n",regname[rs]);
840 output_w32(0xe1700000|rd_rn_rm(0,rs,HOST_TEMPREG));
841 }
842}
843
e2b5e7aa 844static void emit_cmovne_imm(int imm,int rt)
57871462 845{
846 assem_debug("movne %s,#%d\n",regname[rt],imm);
847 u_int armval;
cfbd3c6e 848 genimm_checked(imm,&armval);
57871462 849 output_w32(0x13a00000|rd_rn_rm(rt,0,0)|armval);
850}
e2b5e7aa 851
852static void emit_cmovl_imm(int imm,int rt)
57871462 853{
854 assem_debug("movlt %s,#%d\n",regname[rt],imm);
855 u_int armval;
cfbd3c6e 856 genimm_checked(imm,&armval);
57871462 857 output_w32(0xb3a00000|rd_rn_rm(rt,0,0)|armval);
858}
e2b5e7aa 859
860static void emit_cmovb_imm(int imm,int rt)
57871462 861{
862 assem_debug("movcc %s,#%d\n",regname[rt],imm);
863 u_int armval;
cfbd3c6e 864 genimm_checked(imm,&armval);
57871462 865 output_w32(0x33a00000|rd_rn_rm(rt,0,0)|armval);
866}
e2b5e7aa 867
3968e69e 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
9c997d19 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
de6dbc52 884static unused void emit_cmovne_reg(int rs,int rt)
57871462 885{
886 assem_debug("movne %s,%s\n",regname[rt],regname[rs]);
887 output_w32(0x11a00000|rd_rn_rm(rt,0,rs));
888}
e2b5e7aa 889
890static void emit_cmovl_reg(int rs,int rt)
57871462 891{
892 assem_debug("movlt %s,%s\n",regname[rt],regname[rs]);
893 output_w32(0xb1a00000|rd_rn_rm(rt,0,rs));
894}
e2b5e7aa 895
e3c6bdb5 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
e2b5e7aa 902static void emit_cmovs_reg(int rs,int rt)
57871462 903{
904 assem_debug("movmi %s,%s\n",regname[rt],regname[rs]);
905 output_w32(0x41a00000|rd_rn_rm(rt,0,rs));
906}
907
e2b5e7aa 908static void emit_slti32(int rs,int imm,int rt)
57871462 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}
e2b5e7aa 915
916static void emit_sltiu32(int rs,int imm,int rt)
57871462 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}
e2b5e7aa 923
e2b5e7aa 924static void emit_cmp(int rs,int rt)
57871462 925{
926 assem_debug("cmp %s,%s\n",regname[rs],regname[rt]);
927 output_w32(0xe1500000|rd_rn_rm(0,rs,rt));
928}
e2b5e7aa 929
882a08fc 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
e2b5e7aa 936static void emit_set_gz32(int rs, int rt)
57871462 937{
938 //assem_debug("set_gz32\n");
939 emit_cmpimm(rs,1);
940 emit_movimm(1,rt);
941 emit_cmovl_imm(0,rt);
942}
e2b5e7aa 943
944static void emit_set_nz32(int rs, int rt)
57871462 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}
e2b5e7aa 951
e2b5e7aa 952static void emit_set_if_less32(int rs1, int rs2, int rt)
57871462 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}
e2b5e7aa 960
961static void emit_set_if_carry32(int rs1, int rs2, int rt)
57871462 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}
e2b5e7aa 969
2a014d73 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
643aeae3 976static void emit_call(const void *a_)
57871462 977{
643aeae3 978 int a = (int)a_;
d1e4ebd9 979 assem_debug("bl %x (%x+%x)%s\n",a,(int)out,a-(int)out-8,func_name(a_));
57871462 980 u_int offset=genjmp(a);
981 output_w32(0xeb000000|offset);
982}
e2b5e7aa 983
b14b6a8f 984static void emit_jmp(const void *a_)
57871462 985{
b14b6a8f 986 int a = (int)a_;
d1e4ebd9 987 assem_debug("b %x (%x+%x)%s\n",a,(int)out,a-(int)out-8,func_name(a_));
57871462 988 u_int offset=genjmp(a);
989 output_w32(0xea000000|offset);
990}
e2b5e7aa 991
643aeae3 992static void emit_jne(const void *a_)
57871462 993{
643aeae3 994 int a = (int)a_;
57871462 995 assem_debug("bne %x\n",a);
996 u_int offset=genjmp(a);
997 output_w32(0x1a000000|offset);
998}
e2b5e7aa 999
7c3a5182 1000static void emit_jeq(const void *a_)
57871462 1001{
7c3a5182 1002 int a = (int)a_;
57871462 1003 assem_debug("beq %x\n",a);
1004 u_int offset=genjmp(a);
1005 output_w32(0x0a000000|offset);
1006}
e2b5e7aa 1007
7c3a5182 1008static void emit_js(const void *a_)
57871462 1009{
7c3a5182 1010 int a = (int)a_;
57871462 1011 assem_debug("bmi %x\n",a);
1012 u_int offset=genjmp(a);
1013 output_w32(0x4a000000|offset);
1014}
e2b5e7aa 1015
7c3a5182 1016static void emit_jns(const void *a_)
57871462 1017{
7c3a5182 1018 int a = (int)a_;
57871462 1019 assem_debug("bpl %x\n",a);
1020 u_int offset=genjmp(a);
1021 output_w32(0x5a000000|offset);
1022}
e2b5e7aa 1023
7c3a5182 1024static void emit_jl(const void *a_)
57871462 1025{
7c3a5182 1026 int a = (int)a_;
57871462 1027 assem_debug("blt %x\n",a);
1028 u_int offset=genjmp(a);
1029 output_w32(0xba000000|offset);
1030}
e2b5e7aa 1031
7c3a5182 1032static void emit_jge(const void *a_)
57871462 1033{
7c3a5182 1034 int a = (int)a_;
57871462 1035 assem_debug("bge %x\n",a);
1036 u_int offset=genjmp(a);
1037 output_w32(0xaa000000|offset);
1038}
e2b5e7aa 1039
a5cd72d0 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
7c3a5182 1048static void emit_jno(const void *a_)
57871462 1049{
7c3a5182 1050 int a = (int)a_;
57871462 1051 assem_debug("bvc %x\n",a);
1052 u_int offset=genjmp(a);
1053 output_w32(0x7a000000|offset);
1054}
e2b5e7aa 1055
7c3a5182 1056static void emit_jc(const void *a_)
57871462 1057{
7c3a5182 1058 int a = (int)a_;
57871462 1059 assem_debug("bcs %x\n",a);
1060 u_int offset=genjmp(a);
1061 output_w32(0x2a000000|offset);
1062}
e2b5e7aa 1063
7c3a5182 1064static void emit_jcc(const void *a_)
57871462 1065{
b14b6a8f 1066 int a = (int)a_;
57871462 1067 assem_debug("bcc %x\n",a);
1068 u_int offset=genjmp(a);
1069 output_w32(0x3a000000|offset);
1070}
1071
9b495f6e 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
3968e69e 1081static unused void emit_callreg(u_int r)
57871462 1082{
c6c3b1b3 1083 assert(r<15);
1084 assem_debug("blx %s\n",regname[r]);
1085 output_w32(0xe12fff30|r);
57871462 1086}
e2b5e7aa 1087
1088static void emit_jmpreg(u_int r)
57871462 1089{
1090 assem_debug("mov pc,%s\n",regname[r]);
1091 output_w32(0xe1a00000|rd_rn_rm(15,0,r));
1092}
1093
be516ebe 1094static void emit_ret(void)
1095{
1096 emit_jmpreg(14);
1097}
1098
e2b5e7aa 1099static void emit_readword_indexed(int offset, int rs, int rt)
57871462 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}
e2b5e7aa 1109
1110static void emit_readword_dualindexedx4(int rs1, int rs2, int rt)
57871462 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}
39b71d9a 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}
e2b5e7aa 1122
1123static void emit_ldrcc_dualindexed(int rs1, int rs2, int rt)
c6c3b1b3 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}
e2b5e7aa 1128
37387d8b 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
e2b5e7aa 1135static void emit_ldrccb_dualindexed(int rs1, int rs2, int rt)
c6c3b1b3 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}
e2b5e7aa 1140
37387d8b 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
e2b5e7aa 1147static void emit_ldrccsb_dualindexed(int rs1, int rs2, int rt)
c6c3b1b3 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}
e2b5e7aa 1152
37387d8b 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
e2b5e7aa 1159static void emit_ldrcch_dualindexed(int rs1, int rs2, int rt)
c6c3b1b3 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}
e2b5e7aa 1164
37387d8b 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
e2b5e7aa 1171static void emit_ldrccsh_dualindexed(int rs1, int rs2, int rt)
c6c3b1b3 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));
37387d8b 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));
c6c3b1b3 1193}
e2b5e7aa 1194
e2b5e7aa 1195static void emit_movsbl_indexed(int offset, int rs, int rt)
57871462 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}
e2b5e7aa 1205
e2b5e7aa 1206static void emit_movswl_indexed(int offset, int rs, int rt)
57871462 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}
e2b5e7aa 1216
1217static void emit_movzbl_indexed(int offset, int rs, int rt)
57871462 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}
e2b5e7aa 1227
e2b5e7aa 1228static void emit_movzwl_indexed(int offset, int rs, int rt)
57871462 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}
e2b5e7aa 1238
054175e9 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}
e2b5e7aa 1249
643aeae3 1250static void emit_readword(void *addr, int rt)
57871462 1251{
643aeae3 1252 uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
57871462 1253 assert(offset<4096);
a5cd72d0 1254 assem_debug("ldr %s,fp+%#x%s\n", regname[rt], offset, fpofs_name(offset));
57871462 1255 output_w32(0xe5900000|rd_rn_rm(rt,FP,0)|offset);
1256}
39b71d9a 1257#define emit_readptr emit_readword
e2b5e7aa 1258
e2b5e7aa 1259static void emit_writeword_indexed(int rt, int offset, int rs)
57871462 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}
e2b5e7aa 1269
e2b5e7aa 1270static void emit_writehword_indexed(int rt, int offset, int rs)
57871462 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}
e2b5e7aa 1280
1281static void emit_writebyte_indexed(int rt, int offset, int rs)
57871462 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}
e2b5e7aa 1291
e2b5e7aa 1292static void emit_strcc_dualindexed(int rs1, int rs2, int rt)
b96d3df7 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}
e2b5e7aa 1297
1298static void emit_strccb_dualindexed(int rs1, int rs2, int rt)
b96d3df7 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}
e2b5e7aa 1303
1304static void emit_strcch_dualindexed(int rs1, int rs2, int rt)
b96d3df7 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}
e2b5e7aa 1309
643aeae3 1310static void emit_writeword(int rt, void *addr)
57871462 1311{
643aeae3 1312 uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
57871462 1313 assert(offset<4096);
a5cd72d0 1314 assem_debug("str %s,fp+%#x%s\n", regname[rt], offset, fpofs_name(offset));
57871462 1315 output_w32(0xe5800000|rd_rn_rm(rt,FP,0)|offset);
1316}
e2b5e7aa 1317
e2b5e7aa 1318static void emit_umull(u_int rs1, u_int rs2, u_int hi, u_int lo)
57871462 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}
e2b5e7aa 1327
1328static void emit_smull(u_int rs1, u_int rs2, u_int hi, u_int lo)
57871462 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
e2b5e7aa 1338static void emit_clz(int rs,int rt)
57871462 1339{
1340 assem_debug("clz %s,%s\n",regname[rt],regname[rs]);
1341 output_w32(0xe16f0f10|rd_rn_rm(rt,0,rs));
1342}
1343
e2b5e7aa 1344static void emit_subcs(int rs1,int rs2,int rt)
57871462 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
e2b5e7aa 1350static void emit_shrcc_imm(int rs,u_int imm,int rt)
57871462 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
e2b5e7aa 1358static void emit_shrne_imm(int rs,u_int imm,int rt)
b1be1eee 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
e2b5e7aa 1366static void emit_negmi(int rs, int rt)
57871462 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
e2b5e7aa 1372static void emit_negsmi(int rs, int rt)
57871462 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
e2b5e7aa 1378static void emit_bic_lsl(u_int rs1,u_int rs2,u_int shift,u_int rt)
57871462 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
e2b5e7aa 1384static void emit_bic_lsr(u_int rs1,u_int rs2,u_int shift,u_int rt)
57871462 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
e2b5e7aa 1390static void emit_teq(int rs, int rt)
57871462 1391{
1392 assem_debug("teq %s,%s\n",regname[rs],regname[rt]);
1393 output_w32(0xe1300000|rd_rn_rm(0,rs,rt));
1394}
1395
3968e69e 1396static unused void emit_rsbimm(int rs, int imm, int rt)
57871462 1397{
1398 u_int armval;
cfbd3c6e 1399 genimm_checked(imm,&armval);
57871462 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
57871462 1404// Conditionally select one of two immediates, optimizing for small code size
1405// This will only be called if HAVE_CMOV_IMM is defined
e2b5e7aa 1406static void emit_cmov2imm_e_ne_compact(int imm1,int imm2,u_int rt)
57871462 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 {
665f33e1 1419 #ifndef HAVE_ARMV7
57871462 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
57871462 1439// special case for checking invalid_code
9b495f6e 1440static void emit_ldrb_indexedsr12_reg(int base, int r, int rt)
57871462 1441{
9b495f6e 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);
57871462 1444}
1445
e2b5e7aa 1446static void emit_callne(int a)
0bbd1454 1447{
1448 assem_debug("blne %x\n",a);
1449 u_int offset=genjmp(a);
1450 output_w32(0x1b000000|offset);
1451}
1452
57871462 1453// Used to preload hash table entries
e2b5e7aa 1454static unused void emit_prefetchreg(int r)
57871462 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
e2b5e7aa 1461static void emit_ldreq_indexed(int rs, u_int offset, int rt)
57871462 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
e2b5e7aa 1468static void emit_orrne_imm(int rs,int imm,int rt)
b9b61529 1469{
1470 u_int armval;
cfbd3c6e 1471 genimm_checked(imm,&armval);
b9b61529 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
e2b5e7aa 1476static unused void emit_addpl_imm(int rs,int imm,int rt)
665f33e1 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
e2b5e7aa 1484static void emit_jno_unlikely(int a)
57871462 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
054175e9 1491static void save_regs_all(u_int reglist)
57871462 1492{
054175e9 1493 int i;
57871462 1494 if(!reglist) return;
1495 assem_debug("stmia fp,{");
054175e9 1496 for(i=0;i<16;i++)
1497 if(reglist&(1<<i))
1498 assem_debug("r%d,",i);
57871462 1499 assem_debug("}\n");
1500 output_w32(0xe88b0000|reglist);
1501}
e2b5e7aa 1502
054175e9 1503static void restore_regs_all(u_int reglist)
57871462 1504{
054175e9 1505 int i;
57871462 1506 if(!reglist) return;
1507 assem_debug("ldmia fp,{");
054175e9 1508 for(i=0;i<16;i++)
1509 if(reglist&(1<<i))
1510 assem_debug("r%d,",i);
57871462 1511 assem_debug("}\n");
1512 output_w32(0xe89b0000|reglist);
1513}
e2b5e7aa 1514
054175e9 1515// Save registers before function call
1516static void save_regs(u_int reglist)
1517{
4d646738 1518 reglist&=CALLER_SAVE_REGS; // only save the caller-save registers, r0-r3, r12
054175e9 1519 save_regs_all(reglist);
1520}
e2b5e7aa 1521
054175e9 1522// Restore registers after function call
1523static void restore_regs(u_int reglist)
1524{
4d646738 1525 reglist&=CALLER_SAVE_REGS;
054175e9 1526 restore_regs_all(reglist);
1527}
57871462 1528
57871462 1529/* Stubs/epilogue */
1530
e2b5e7aa 1531static void literal_pool(int n)
57871462 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 {
77750690 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 }
57871462 1550 ptr=(u_int *)literals[i][0];
77750690 1551 u_int offset=l_addr-(u_int)ptr-8;
57871462 1552 assert(offset<4096);
1553 assert(!(offset&3));
1554 *ptr|=offset;
77750690 1555 if(l_addr==(u_int)out) {
1556 literals[i][0]=l_addr; // remember for dupes
1557 output_w32(literals[i][1]);
1558 }
57871462 1559 }
1560 literalcount=0;
1561}
1562
e2b5e7aa 1563static void literal_pool_jumpover(int n)
57871462 1564{
1565 if(!literalcount) return;
1566 if(n) {
1567 if((int)out-literals[0][0]<4096-n) return;
1568 }
df4dc2b1 1569 void *jaddr = out;
57871462 1570 emit_jmp(0);
1571 literal_pool(0);
df4dc2b1 1572 set_jump_target(jaddr, out);
57871462 1573}
1574
7c3a5182 1575// parsed by get_pointer, find_extjump_insn
104df9d3 1576static void emit_extjump(u_char *addr, u_int target)
57871462 1577{
1578 u_char *ptr=(u_char *)addr;
1579 assert((ptr[3]&0x0e)==0xa);
e2b5e7aa 1580 (void)ptr;
1581
57871462 1582 emit_loadlp(target,0);
643aeae3 1583 emit_loadlp((u_int)addr,1);
66ea165f 1584 assert(ndrc->translation_cache <= addr &&
1585 addr < ndrc->translation_cache + sizeof(ndrc->translation_cache));
104df9d3 1586 emit_far_jump(dyna_linker);
57871462 1587}
1588
d1e4ebd9 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
13e35c04 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{
8575a877 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{
13e35c04 1627 u_int xs;
8575a877 1628 int diff;
1629 if(v1==v2) return 1;
1630 diff=v2-v1;
1631 for(xs=diff;xs!=0&&(xs&3)==0;xs>>=2)
13e35c04 1632 ;
8575a877 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;
13e35c04 1638}
cbbab9cd 1639
b14b6a8f 1640static void mov_loadtype_adj(enum stub_type type,int rs,int rt)
b1be1eee 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
b1be1eee 1652#include "pcsxmem.h"
1653#include "pcsxmem_inline.c"
b1be1eee 1654
e2b5e7aa 1655static void do_readstub(int n)
57871462 1656{
b14b6a8f 1657 assem_debug("do_readstub %x\n",start+stubs[n].a*4);
57871462 1658 literal_pool(256);
b14b6a8f 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;
81dbbf4c 1663 const struct regstat *i_regs=(struct regstat *)stubs[n].c;
b14b6a8f 1664 u_int reglist=stubs[n].e;
81dbbf4c 1665 const signed char *i_regmap=i_regs->regmap;
581335b0 1666 int rt;
a5cd72d0 1667 if(dops[i].itype==C2LS||dops[i].itype==LOADLR) {
57871462 1668 rt=get_reg(i_regmap,FTEMP);
1669 }else{
cf95b4f0 1670 rt=get_reg(i_regmap,dops[i].rt1);
57871462 1671 }
1672 assert(rs>=0);
df4dc2b1 1673 int r,temp=-1,temp2=HOST_TEMPREG,regs_saved=0;
1674 void *restore_jump = NULL;
c6c3b1b3 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 }
cf95b4f0 1681 if(rt>=0&&dops[i].rt1!=0)
c6c3b1b3 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;
643aeae3 1690 emit_readword(&mem_rtab,temp);
c6c3b1b3 1691 emit_shrimm(rs,12,temp2);
1692 emit_readword_dualindexedx4(temp,temp2,temp2);
1693 emit_lsls_imm(temp2,1,temp2);
a5cd72d0 1694 if(dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) {
c6c3b1b3 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;
b14b6a8f 1701 default: assert(0);
c6c3b1b3 1702 }
1703 }
1704 if(regs_saved) {
df4dc2b1 1705 restore_jump=out;
c6c3b1b3 1706 emit_jcc(0); // jump to reg restore
1707 }
1708 else
b14b6a8f 1709 emit_jcc(stubs[n].retaddr); // return address
c6c3b1b3 1710
1711 if(!regs_saved)
1712 save_regs(reglist);
643aeae3 1713 void *handler=NULL;
c6c3b1b3 1714 if(type==LOADB_STUB||type==LOADBU_STUB)
643aeae3 1715 handler=jump_handler_read8;
c6c3b1b3 1716 if(type==LOADH_STUB||type==LOADHU_STUB)
643aeae3 1717 handler=jump_handler_read16;
c6c3b1b3 1718 if(type==LOADW_STUB)
643aeae3 1719 handler=jump_handler_read32;
1720 assert(handler);
b96d3df7 1721 pass_args(rs,temp2);
c6c3b1b3 1722 int cc=get_reg(i_regmap,CCREG);
1723 if(cc<0)
1724 emit_loadreg(CCREG,2);
2330734f 1725 emit_addimm(cc<0?2:cc,(int)stubs[n].d,2);
2a014d73 1726 emit_far_call(handler);
a5cd72d0 1727 if(dops[i].itype==C2LS||(rt>=0&&dops[i].rt1!=0)) {
b1be1eee 1728 mov_loadtype_adj(type,0,rt);
c6c3b1b3 1729 }
1730 if(restore_jump)
df4dc2b1 1731 set_jump_target(restore_jump, out);
c6c3b1b3 1732 restore_regs(reglist);
b14b6a8f 1733 emit_jmp(stubs[n].retaddr); // return address
57871462 1734}
1735
81dbbf4c 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)
57871462 1738{
277718fa 1739 int ra = cinfo[i].addr;
1740 int rt = get_reg(regmap,target);
1741 assert(ra >= 0);
2a014d73 1742 u_int is_dynamic;
687b4580 1743 uintptr_t host_addr = 0;
643aeae3 1744 void *handler;
b1be1eee 1745 int cc=get_reg(regmap,CCREG);
277718fa 1746 if(pcsx_direct_read(type,addr,adj,cc,target?ra:-1,rt))
b1be1eee 1747 return;
643aeae3 1748 handler = get_direct_memhandler(mem_rtab, addr, type, &host_addr);
1749 if (handler == NULL) {
cf95b4f0 1750 if(rt<0||dops[i].rt1==0)
c6c3b1b3 1751 return;
13e35c04 1752 if(addr!=host_addr)
277718fa 1753 emit_movimm_from(addr,ra,host_addr,ra);
c6c3b1b3 1754 switch(type) {
277718fa 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;
c6c3b1b3 1760 default: assert(0);
1761 }
1762 return;
1763 }
b1be1eee 1764 is_dynamic=pcsxmem_is_handler_dynamic(addr);
1765 if(is_dynamic) {
1766 if(type==LOADB_STUB||type==LOADBU_STUB)
643aeae3 1767 handler=jump_handler_read8;
b1be1eee 1768 if(type==LOADH_STUB||type==LOADHU_STUB)
643aeae3 1769 handler=jump_handler_read16;
b1be1eee 1770 if(type==LOADW_STUB)
643aeae3 1771 handler=jump_handler_read32;
b1be1eee 1772 }
c6c3b1b3 1773
1774 // call a memhandler
cf95b4f0 1775 if(rt>=0&&dops[i].rt1!=0)
c6c3b1b3 1776 reglist&=~(1<<rt);
1777 save_regs(reglist);
1778 if(target==0)
1779 emit_movimm(addr,0);
277718fa 1780 else if(ra!=0)
1781 emit_mov(ra,0);
b1be1eee 1782 if(cc<0)
1783 emit_loadreg(CCREG,2);
1784 if(is_dynamic) {
1785 emit_movimm(((u_int *)mem_rtab)[addr>>12]<<1,1);
2330734f 1786 emit_addimm(cc<0?2:cc,adj,2);
c6c3b1b3 1787 }
b1be1eee 1788 else {
643aeae3 1789 emit_readword(&last_count,3);
2330734f 1790 emit_addimm(cc<0?2:cc,adj,2);
b1be1eee 1791 emit_add(2,3,2);
d7546062 1792 emit_writeword(2,&psxRegs.cycle);
b1be1eee 1793 }
1794
2a014d73 1795 emit_far_call(handler);
b1be1eee 1796
cf95b4f0 1797 if(rt>=0&&dops[i].rt1!=0) {
c6c3b1b3 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);
57871462 1808}
1809
e2b5e7aa 1810static void do_writestub(int n)
57871462 1811{
b14b6a8f 1812 assem_debug("do_writestub %x\n",start+stubs[n].a*4);
57871462 1813 literal_pool(256);
b14b6a8f 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;
81dbbf4c 1818 const struct regstat *i_regs=(struct regstat *)stubs[n].c;
b14b6a8f 1819 u_int reglist=stubs[n].e;
81dbbf4c 1820 const signed char *i_regmap=i_regs->regmap;
581335b0 1821 int rt,r;
a5cd72d0 1822 if(dops[i].itype==C2LS) {
57871462 1823 rt=get_reg(i_regmap,r=FTEMP);
1824 }else{
cf95b4f0 1825 rt=get_reg(i_regmap,r=dops[i].rs2);
57871462 1826 }
1827 assert(rs>=0);
1828 assert(rt>=0);
b14b6a8f 1829 int rtmp,temp=-1,temp2=HOST_TEMPREG,regs_saved=0;
df4dc2b1 1830 void *restore_jump = NULL;
b96d3df7 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;
643aeae3 1846 emit_readword(&mem_wtab,temp);
b96d3df7 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) {
df4dc2b1 1857 restore_jump=out;
b96d3df7 1858 emit_jcc(0); // jump to reg restore
1859 }
1860 else
b14b6a8f 1861 emit_jcc(stubs[n].retaddr); // return address (invcode check)
b96d3df7 1862
1863 if(!regs_saved)
1864 save_regs(reglist);
643aeae3 1865 void *handler=NULL;
b96d3df7 1866 switch(type) {
643aeae3 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;
b14b6a8f 1870 default: assert(0);
b96d3df7 1871 }
643aeae3 1872 assert(handler);
b96d3df7 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);
2330734f 1879 emit_addimm(cc<0?2:cc,(int)stubs[n].d,2);
b96d3df7 1880 // returns new cycle_count
2a014d73 1881 emit_far_call(handler);
2330734f 1882 emit_addimm(0,-(int)stubs[n].d,cc<0?2:cc);
b96d3df7 1883 if(cc<0)
1884 emit_storereg(CCREG,2);
1885 if(restore_jump)
df4dc2b1 1886 set_jump_target(restore_jump, out);
b96d3df7 1887 restore_regs(reglist);
b14b6a8f 1888 emit_jmp(stubs[n].retaddr);
57871462 1889}
1890
81dbbf4c 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)
57871462 1893{
277718fa 1894 int ra = cinfo[i].addr;
1895 int rt = get_reg(regmap, target);
1896 assert(ra>=0);
57871462 1897 assert(rt>=0);
687b4580 1898 uintptr_t host_addr = 0;
643aeae3 1899 void *handler = get_direct_memhandler(mem_wtab, addr, type, &host_addr);
1900 if (handler == NULL) {
13e35c04 1901 if(addr!=host_addr)
277718fa 1902 emit_movimm_from(addr,ra,host_addr,ra);
b96d3df7 1903 switch(type) {
277718fa 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;
b96d3df7 1907 default: assert(0);
1908 }
1909 return;
1910 }
1911
1912 // call a memhandler
1913 save_regs(reglist);
277718fa 1914 pass_args(ra,rt);
b96d3df7 1915 int cc=get_reg(regmap,CCREG);
1916 if(cc<0)
1917 emit_loadreg(CCREG,2);
2330734f 1918 emit_addimm(cc<0?2:cc,adj,2);
643aeae3 1919 emit_movimm((u_int)handler,3);
b96d3df7 1920 // returns new cycle_count
2a014d73 1921 emit_far_call(jump_handler_write_h);
2330734f 1922 emit_addimm(0,-adj,cc<0?2:cc);
b96d3df7 1923 if(cc<0)
1924 emit_storereg(CCREG,2);
1925 restore_regs(reglist);
57871462 1926}
1927
57871462 1928/* Special assem */
1929
81dbbf4c 1930static void c2op_prologue(u_int op, int i, const struct regstat *i_regs, u_int reglist)
054175e9 1931{
1932 save_regs_all(reglist);
32631e6a 1933 cop2_do_stall_check(op, i, i_regs, 0);
82ed88eb 1934#ifdef PCNT
81dbbf4c 1935 emit_movimm(op, 0);
2a014d73 1936 emit_far_call(pcnt_gte_start);
82ed88eb 1937#endif
81dbbf4c 1938 emit_addimm(FP, (u_char *)&psxRegs.CP2D.r[0] - (u_char *)&dynarec_local, 0); // cop2 regs
054175e9 1939}
1940
1941static void c2op_epilogue(u_int op,u_int reglist)
1942{
82ed88eb 1943#ifdef PCNT
1944 emit_movimm(op,0);
2a014d73 1945 emit_far_call(pcnt_gte_end);
82ed88eb 1946#endif
054175e9 1947 restore_regs_all(reglist);
1948}
1949
6c0eefaf 1950static void c2op_call_MACtoIR(int lm,int need_flags)
1951{
1952 if(need_flags)
2a014d73 1953 emit_far_call(lm?gteMACtoIR_lm1:gteMACtoIR_lm0);
6c0eefaf 1954 else
2a014d73 1955 emit_far_call(lm?gteMACtoIR_lm1_nf:gteMACtoIR_lm0_nf);
6c0eefaf 1956}
1957
1958static void c2op_call_rgb_func(void *func,int lm,int need_ir,int need_flags)
1959{
2a014d73 1960 emit_far_call(func);
6c0eefaf 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);
2a014d73 1965 emit_far_call(need_flags?gteMACtoRGB:gteMACtoRGB_nf);
6c0eefaf 1966}
1967
81dbbf4c 1968static void c2op_assemble(int i, const struct regstat *i_regs)
b9b61529 1969{
81dbbf4c 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;
b9b61529 1974
1975 if (gte_handlers[c2op]!=NULL) {
bedfea38 1976 need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
054175e9 1977 need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
cbbd8dd7 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);
81dbbf4c 1980 if(HACK_ENABLED(NDHACK_GTE_NO_FLAGS))
0ff8c62c 1981 need_flags=0;
6c0eefaf 1982 int shift = (source[i] >> 19) & 1;
1983 int lm = (source[i] >> 10) & 1;
054175e9 1984 switch(c2op) {
19776aef 1985#ifndef DRC_DBG
054175e9 1986 case GTE_MVMVA: {
82336ba3 1987#ifdef HAVE_ARMV5
054175e9 1988 int v = (source[i] >> 15) & 3;
1989 int cv = (source[i] >> 13) & 3;
1990 int mx = (source[i] >> 17) & 3;
4d646738 1991 reglist=reglist_full&(CALLER_SAVE_REGS|0xf0); // +{r4-r7}
81dbbf4c 1992 c2op_prologue(c2op,i,i_regs,reglist);
054175e9 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
643aeae3 2005 emit_readword(&zeromem_ptr,6);
054175e9 2006 if(cv<3)
2007 emit_addimm(0,32*4+(cv*8+5)*4,7);
2008 else
643aeae3 2009 emit_readword(&zeromem_ptr,7);
054175e9 2010#ifdef __ARM_NEON__
2011 emit_movimm(source[i],1); // opcode
2a014d73 2012 emit_far_call(gteMVMVA_part_neon);
054175e9 2013 if(need_flags) {
2014 emit_movimm(lm,1);
2a014d73 2015 emit_far_call(gteMACtoIR_flags_neon);
054175e9 2016 }
2017#else
2018 if(cv==3&&shift)
33788798 2019 emit_far_call(gteMVMVA_part_cv3sh12_arm);
054175e9 2020 else {
2021 emit_movimm(shift,1);
33788798 2022 emit_far_call(need_flags?gteMVMVA_part_arm:gteMVMVA_part_nf_arm);
054175e9 2023 }
6c0eefaf 2024 if(need_flags||need_ir)
2025 c2op_call_MACtoIR(lm,need_flags);
82336ba3 2026#endif
2027#else /* if not HAVE_ARMV5 */
81dbbf4c 2028 c2op_prologue(c2op,i,i_regs,reglist);
82336ba3 2029 emit_movimm(source[i],1); // opcode
643aeae3 2030 emit_writeword(1,&psxRegs.code);
2a014d73 2031 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
054175e9 2032#endif
2033 break;
2034 }
6c0eefaf 2035 case GTE_OP:
81dbbf4c 2036 c2op_prologue(c2op,i,i_regs,reglist);
2a014d73 2037 emit_far_call(shift?gteOP_part_shift:gteOP_part_noshift);
6c0eefaf 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:
81dbbf4c 2044 c2op_prologue(c2op,i,i_regs,reglist);
6c0eefaf 2045 c2op_call_rgb_func(shift?gteDPCS_part_shift:gteDPCS_part_noshift,lm,need_ir,need_flags);
2046 break;
2047 case GTE_INTPL:
81dbbf4c 2048 c2op_prologue(c2op,i,i_regs,reglist);
6c0eefaf 2049 c2op_call_rgb_func(shift?gteINTPL_part_shift:gteINTPL_part_noshift,lm,need_ir,need_flags);
2050 break;
2051 case GTE_SQR:
81dbbf4c 2052 c2op_prologue(c2op,i,i_regs,reglist);
2a014d73 2053 emit_far_call(shift?gteSQR_part_shift:gteSQR_part_noshift);
6c0eefaf 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:
81dbbf4c 2060 c2op_prologue(c2op,i,i_regs,reglist);
6c0eefaf 2061 c2op_call_rgb_func(gteDCPL_part,lm,need_ir,need_flags);
2062 break;
2063 case GTE_GPF:
81dbbf4c 2064 c2op_prologue(c2op,i,i_regs,reglist);
6c0eefaf 2065 c2op_call_rgb_func(shift?gteGPF_part_shift:gteGPF_part_noshift,lm,need_ir,need_flags);
2066 break;
2067 case GTE_GPL:
81dbbf4c 2068 c2op_prologue(c2op,i,i_regs,reglist);
6c0eefaf 2069 c2op_call_rgb_func(shift?gteGPL_part_shift:gteGPL_part_noshift,lm,need_ir,need_flags);
2070 break;
19776aef 2071#endif
054175e9 2072 default:
81dbbf4c 2073 c2op_prologue(c2op,i,i_regs,reglist);
19776aef 2074#ifdef DRC_DBG
2075 emit_movimm(source[i],1); // opcode
643aeae3 2076 emit_writeword(1,&psxRegs.code);
19776aef 2077#endif
2a014d73 2078 emit_far_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
054175e9 2079 break;
2080 }
2081 c2op_epilogue(c2op,reglist);
2082 }
b9b61529 2083}
2084
3968e69e 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);
9c997d19 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);
3968e69e 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);
9c997d19 2114 emit_shrimm(temp,7+16,tl);
3968e69e 2115 do_mfc2_31_one(10,temp);
9c997d19 2116 emit_orrshr_imm(temp,2+16,tl);
3968e69e 2117 do_mfc2_31_one(11,temp);
9c997d19 2118 emit_orrshr_imm(temp,-3+16,tl);
3968e69e 2119 emit_writeword(tl,&reg_cop2d[29]);
2120 if (temp == HOST_TEMPREG)
2121 host_tempreg_release();
2122}
2123
2330734f 2124static void multdiv_assemble_arm(int i, const struct regstat *i_regs)
57871462 2125{
2126 // case 0x18: MULT
2127 // case 0x19: MULTU
2128 // case 0x1A: DIV
2129 // case 0x1B: DIVU
cf95b4f0 2130 if(dops[i].rs1&&dops[i].rs2)
57871462 2131 {
a5cd72d0 2132 switch (dops[i].opcode2)
57871462 2133 {
a5cd72d0 2134 case 0x18: // MULT
57871462 2135 {
cf95b4f0 2136 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2137 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
57871462 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 }
a5cd72d0 2146 break;
2147 case 0x19: // MULTU
57871462 2148 {
cf95b4f0 2149 signed char m1=get_reg(i_regs->regmap,dops[i].rs1);
2150 signed char m2=get_reg(i_regs->regmap,dops[i].rs2);
57871462 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 }
a5cd72d0 2159 break;
2160 case 0x1A: // DIV
57871462 2161 {
cf95b4f0 2162 signed char d1=get_reg(i_regs->regmap,dops[i].rs1);
2163 signed char d2=get_reg(i_regs->regmap,dops[i].rs2);
57871462 2164 signed char quotient=get_reg(i_regs->regmap,LOREG);
2165 signed char remainder=get_reg(i_regs->regmap,HIREG);
a5cd72d0 2166 void *jaddr_div0;
2167 assert(d1>=0);
2168 assert(d2>=0);
57871462 2169 assert(quotient>=0);
2170 assert(remainder>=0);
2171 emit_movs(d1,remainder);
44a80f6a 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)
57871462 2175 emit_movs(d2,HOST_TEMPREG);
a5cd72d0 2176 jaddr_div0 = out;
2177 emit_jeq(0); // Division by zero
82336ba3 2178 emit_negsmi(HOST_TEMPREG,HOST_TEMPREG);
665f33e1 2179#ifdef HAVE_ARMV5
57871462 2180 emit_clz(HOST_TEMPREG,quotient);
a5cd72d0 2181 emit_shl(HOST_TEMPREG,quotient,HOST_TEMPREG); // shifted divisor
665f33e1 2182#else
2183 emit_movimm(0,quotient);
2184 emit_addpl_imm(quotient,1,quotient);
2185 emit_lslpls_imm(HOST_TEMPREG,1,HOST_TEMPREG);
7c3a5182 2186 emit_jns(out-2*4);
665f33e1 2187#endif
57871462 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);
b14b6a8f 2194 emit_jcc(out-16); // -4
57871462 2195 emit_teq(d1,d2);
2196 emit_negmi(quotient,quotient);
a5cd72d0 2197 set_jump_target(jaddr_div0, out);
57871462 2198 emit_test(d1,d1);
2199 emit_negmi(remainder,remainder);
2200 }
a5cd72d0 2201 break;
2202 case 0x1B: // DIVU
57871462 2203 {
cf95b4f0 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
57871462 2206 signed char quotient=get_reg(i_regs->regmap,LOREG);
2207 signed char remainder=get_reg(i_regs->regmap,HIREG);
a5cd72d0 2208 void *jaddr_div0;
2209 assert(d1>=0);
2210 assert(d2>=0);
57871462 2211 assert(quotient>=0);
2212 assert(remainder>=0);
44a80f6a 2213 emit_mov(d1,remainder);
2214 emit_movimm(0xffffffff,quotient); // div0 case
57871462 2215 emit_test(d2,d2);
a5cd72d0 2216 jaddr_div0 = out;
2217 emit_jeq(0); // Division by zero
665f33e1 2218#ifdef HAVE_ARMV5
57871462 2219 emit_clz(d2,HOST_TEMPREG);
2220 emit_movimm(1<<31,quotient);
2221 emit_shl(d2,HOST_TEMPREG,d2);
665f33e1 2222#else
2223 emit_movimm(0,HOST_TEMPREG);
82336ba3 2224 emit_addpl_imm(HOST_TEMPREG,1,HOST_TEMPREG);
2225 emit_lslpls_imm(d2,1,d2);
7c3a5182 2226 emit_jns(out-2*4);
665f33e1 2227 emit_movimm(1<<31,quotient);
2228#endif
57871462 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);
b14b6a8f 2234 emit_jcc(out-16); // -4
a5cd72d0 2235 set_jump_target(jaddr_div0, out);
57871462 2236 }
a5cd72d0 2237 break;
57871462 2238 }
57871462 2239 }
2240 else
2241 {
57871462 2242 signed char hr=get_reg(i_regs->regmap,HIREG);
2243 signed char lr=get_reg(i_regs->regmap,LOREG);
a5cd72d0 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 }
57871462 2283 }
2284}
2285#define multdiv_assemble multdiv_assemble_arm
2286
d1e4ebd9 2287static void do_jump_vaddr(int rs)
2288{
2a014d73 2289 emit_far_jump(jump_vaddr_reg[rs]);
d1e4ebd9 2290}
2291
e2b5e7aa 2292static void do_preload_rhash(int r) {
57871462 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
e2b5e7aa 2297static void do_preload_rhtbl(int ht) {
57871462 2298 emit_addimm(FP,(int)&mini_ht-(int)&dynarec_local,ht);
2299}
2300
e2b5e7aa 2301static void do_rhash(int rs,int rh) {
57871462 2302 emit_andimm(rs,0xf8,rh);
2303}
2304
e2b5e7aa 2305static void do_miniht_load(int ht,int rh) {
57871462 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
e2b5e7aa 2310static void do_miniht_jump(int rs,int rh,int ht) {
57871462 2311 emit_cmp(rh,rs);
2312 emit_ldreq_indexed(ht,4,15);
2313 #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
d1e4ebd9 2314 if(rs!=7)
2315 emit_mov(rs,7);
2316 rs=7;
57871462 2317 #endif
d1e4ebd9 2318 do_jump_vaddr(rs);
57871462 2319}
2320
e2b5e7aa 2321static void do_miniht_insert(u_int return_address,int rt,int temp) {
665f33e1 2322 #ifndef HAVE_ARMV7
57871462 2323 emit_movimm(return_address,rt); // PC into link register
643aeae3 2324 add_to_linker(out,return_address,1);
57871462 2325 emit_pcreladdr(temp);
643aeae3 2326 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
2327 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
57871462 2328 #else
2329 emit_movw(return_address&0x0000FFFF,rt);
643aeae3 2330 add_to_linker(out,return_address,1);
57871462 2331 emit_pcreladdr(temp);
643aeae3 2332 emit_writeword(temp,&mini_ht[(return_address&0xFF)>>3][1]);
57871462 2333 emit_movt(return_address&0xFFFF0000,rt);
643aeae3 2334 emit_writeword(rt,&mini_ht[(return_address&0xFF)>>3][0]);
57871462 2335 #endif
2336}
2337
57871462 2338// CPU-architecture-specific initialization
2a014d73 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));
57871462 2350}
b9b61529 2351
2352// vim:shiftwidth=2:expandtab