timers implemented for new z80 mode
[picodrive.git] / Pico / carthw / svp / compiler.c
CommitLineData
65ca3034 1// SSP1601 to ARM recompiler
2
3// (c) Copyright 2008, Grazvydas "notaz" Ignotas
4// Free for non-commercial use.
726bbb3e 5
6#include "../../PicoInt.h"
e807ac75 7#include "compiler.h"
726bbb3e 8
71bb1b7b 9#define u32 unsigned int
10
71bb1b7b 11static u32 *tcache_ptr = NULL;
726bbb3e 12
726bbb3e 13static int nblocks = 0;
71bb1b7b 14static int n_in_ops = 0;
15
16extern ssp1601_t *ssp;
17
18#define rPC ssp->gr[SSP_PC].h
19#define rPMC ssp->gr[SSP_PMC]
20
21#define SSP_FLAG_Z (1<<0xd)
22#define SSP_FLAG_N (1<<0xf)
726bbb3e 23
5d817c91 24#ifndef ARM
0b5e8296 25#define DUMP_BLOCK 0x0c9a
1ca2ea4f 26u32 *ssp_block_table[0x5090/2];
27u32 *ssp_block_table_iram[15][0x800/2];
28u32 tcache[SSP_TCACHE_SIZE/4];
45883918 29void ssp_drc_next(void){}
30void ssp_drc_next_patch(void){}
31void ssp_drc_end(void){}
5d817c91 32#endif
33
5c129565 34#include "gen_arm.c"
726bbb3e 35
36// -----------------------------------------------------
37
71bb1b7b 38static int get_inc(int mode)
892b1dd2 39{
71bb1b7b 40 int inc = (mode >> 11) & 7;
41 if (inc != 0) {
42 if (inc != 7) inc--;
43 inc = 1 << inc; // 0 1 2 4 8 16 32 128
44 if (mode & 0x8000) inc = -inc; // decrement mode
892b1dd2 45 }
71bb1b7b 46 return inc;
892b1dd2 47}
48
ee9ee9fd 49u32 ssp_pm_read(int reg)
d5276282 50{
51 u32 d = 0, mode;
52
53 if (ssp->emu_status & SSP_PMC_SET)
54 {
55 ssp->pmac_read[reg] = rPMC.v;
56 ssp->emu_status &= ~SSP_PMC_SET;
d5276282 57 return 0;
58 }
59
d5276282 60 // just in case
61 ssp->emu_status &= ~SSP_PMC_HAVE_ADDR;
62
63 mode = ssp->pmac_read[reg]>>16;
64 if ((mode & 0xfff0) == 0x0800) // ROM
65 {
66 d = ((unsigned short *)Pico.rom)[ssp->pmac_read[reg]&0xfffff];
67 ssp->pmac_read[reg] += 1;
68 }
69 else if ((mode & 0x47ff) == 0x0018) // DRAM
70 {
71 unsigned short *dram = (unsigned short *)svp->dram;
72 int inc = get_inc(mode);
73 d = dram[ssp->pmac_read[reg]&0xffff];
74 ssp->pmac_read[reg] += inc;
75 }
76
77 // PMC value corresponds to last PMR accessed
78 rPMC.v = ssp->pmac_read[reg];
79
80 return d;
81}
82
71bb1b7b 83#define overwrite_write(dst, d) \
84{ \
85 if (d & 0xf000) { dst &= ~0xf000; dst |= d & 0xf000; } \
86 if (d & 0x0f00) { dst &= ~0x0f00; dst |= d & 0x0f00; } \
87 if (d & 0x00f0) { dst &= ~0x00f0; dst |= d & 0x00f0; } \
88 if (d & 0x000f) { dst &= ~0x000f; dst |= d & 0x000f; } \
89}
90
ee9ee9fd 91void ssp_pm_write(u32 d, int reg)
d5276282 92{
93 unsigned short *dram;
94 int mode, addr;
95
96 if (ssp->emu_status & SSP_PMC_SET)
97 {
98 ssp->pmac_write[reg] = rPMC.v;
99 ssp->emu_status &= ~SSP_PMC_SET;
100 return;
101 }
102
103 // just in case
104 ssp->emu_status &= ~SSP_PMC_HAVE_ADDR;
105
106 dram = (unsigned short *)svp->dram;
107 mode = ssp->pmac_write[reg]>>16;
108 addr = ssp->pmac_write[reg]&0xffff;
109 if ((mode & 0x43ff) == 0x0018) // DRAM
110 {
111 int inc = get_inc(mode);
112 if (mode & 0x0400) {
113 overwrite_write(dram[addr], d);
114 } else dram[addr] = d;
115 ssp->pmac_write[reg] += inc;
116 }
117 else if ((mode & 0xfbff) == 0x4018) // DRAM, cell inc
118 {
119 if (mode & 0x0400) {
120 overwrite_write(dram[addr], d);
121 } else dram[addr] = d;
34e243f1 122 ssp->pmac_write[reg] += (addr&1) ? 0x1f : 1;
d5276282 123 }
124 else if ((mode & 0x47ff) == 0x001c) // IRAM
125 {
126 int inc = get_inc(mode);
127 ((unsigned short *)svp->iram_rom)[addr&0x3ff] = d;
128 ssp->pmac_write[reg] += inc;
e122fae6 129 ssp->drc.iram_dirty = 1;
d5276282 130 }
131
132 rPMC.v = ssp->pmac_write[reg];
133}
134
135
892b1dd2 136// -----------------------------------------------------
137
0b5e8296 138// 14 IRAM blocks
df143b36 139static unsigned char iram_context_map[] =
140{
141 0, 0, 0, 0, 1, 0, 0, 0, // 04
142 0, 0, 0, 0, 0, 0, 2, 0, // 0e
143 0, 0, 0, 0, 0, 3, 0, 4, // 15 17
144 5, 0, 0, 6, 0, 7, 0, 0, // 18 1b 1d
145 8, 9, 0, 0, 0,10, 0, 0, // 20 21 25
146 0, 0, 0, 0, 0, 0, 0, 0,
147 0, 0,11, 0, 0,12, 0, 0, // 32 35
148 13,14, 0, 0, 0, 0, 0, 0 // 38 39
149};
150
71bb1b7b 151int ssp_get_iram_context(void)
df143b36 152{
153 unsigned char *ir = (unsigned char *)svp->iram_rom;
154 int val1, val = ir[0x083^1] + ir[0x4FA^1] + ir[0x5F7^1] + ir[0x47B^1];
df143b36 155 val1 = iram_context_map[(val>>1)&0x3f];
156
5c129565 157 if (val1 == 0) {
2d2247c2 158 elprintf(EL_ANOMALY, "svp: iram ctx val: %02x PC=%04x\n", (val>>1)&0x3f, rPC);
df143b36 159 //debug_dump2file(name, svp->iram_rom, 0x800);
2d2247c2 160 //exit(1);
df143b36 161 }
df143b36 162 return val1;
163}
164
5d817c91 165// -----------------------------------------------------
0b5e8296 166
5d817c91 167/* regs with known values */
168static struct
169{
170 ssp_reg_t gr[8];
171 unsigned char r[8];
ede7220f 172 unsigned int pmac_read[5];
173 unsigned int pmac_write[5];
d5276282 174 ssp_reg_t pmc;
ede7220f 175 unsigned int emu_status;
bad5731d 176} known_regs;
177
178#define KRREG_X (1 << SSP_X)
179#define KRREG_Y (1 << SSP_Y)
180#define KRREG_A (1 << SSP_A) /* AH only */
181#define KRREG_ST (1 << SSP_ST)
182#define KRREG_STACK (1 << SSP_STACK)
183#define KRREG_PC (1 << SSP_PC)
184#define KRREG_P (1 << SSP_P)
185#define KRREG_PR0 (1 << 8)
186#define KRREG_PR4 (1 << 12)
187#define KRREG_AL (1 << 16)
d5276282 188#define KRREG_PMCM (1 << 18) /* only mode word of PMC */
189#define KRREG_PMC (1 << 19)
ede7220f 190#define KRREG_PM0R (1 << 20)
191#define KRREG_PM1R (1 << 21)
192#define KRREG_PM2R (1 << 22)
193#define KRREG_PM3R (1 << 23)
194#define KRREG_PM4R (1 << 24)
195#define KRREG_PM0W (1 << 25)
196#define KRREG_PM1W (1 << 26)
197#define KRREG_PM2W (1 << 27)
198#define KRREG_PM3W (1 << 28)
199#define KRREG_PM4W (1 << 29)
bad5731d 200
201/* bitfield of known register values */
202static u32 known_regb = 0;
203
204/* known vals, which need to be flushed
d5276282 205 * (only ST, P, r0-r7, PMCx, PMxR, PMxW)
bad5731d 206 * ST means flags are being held in ARM PSR
89fea1e9 207 * P means that it needs to be recalculated
bad5731d 208 */
209static u32 dirty_regb = 0;
5d817c91 210
211/* known values of host regs.
d274c33b 212 * -1 - unknown
213 * 000000-00ffff - 16bit value
214 * 100000-10ffff - base reg (r7) + 16bit val
6e39239f 215 * 0r0000 - means reg (low) eq gr[r].h, r != AL
5d817c91 216 */
217static int hostreg_r[4];
218
219static void hostreg_clear(void)
220{
221 int i;
222 for (i = 0; i < 4; i++)
223 hostreg_r[i] = -1;
224}
225
6e39239f 226static void hostreg_sspreg_changed(int sspreg)
5d817c91 227{
228 int i;
229 for (i = 0; i < 4; i++)
6e39239f 230 if (hostreg_r[i] == (sspreg<<16)) hostreg_r[i] = -1;
5d817c91 231}
232
726bbb3e 233
ede7220f 234#define PROGRAM(x) ((unsigned short *)svp->iram_rom)[x]
235#define PROGRAM_P(x) ((unsigned short *)svp->iram_rom + (x))
726bbb3e 236
ee9ee9fd 237void tr_unhandled(void)
6e39239f 238{
2d2247c2 239 //FILE *f = fopen("tcache.bin", "wb");
240 //fwrite(tcache, 1, (tcache_ptr - tcache)*4, f);
241 //fclose(f);
242 elprintf(EL_ANOMALY, "unhandled @ %04x\n", known_regs.gr[SSP_PC].h<<1);
243 //exit(1);
6e39239f 244}
245
0e4d7ba5 246/* update P, if needed. Trashes r0 */
d274c33b 247static void tr_flush_dirty_P(void)
248{
249 // TODO: const regs
bad5731d 250 if (!(dirty_regb & KRREG_P)) return;
d274c33b 251 EOP_MOV_REG_ASR(10, 4, 16); // mov r10, r4, asr #16
0e4d7ba5 252 EOP_MOV_REG_LSL( 0, 4, 16); // mov r0, r4, lsl #16
253 EOP_MOV_REG_ASR( 0, 0, 15); // mov r0, r0, asr #15
254 EOP_MUL(10, 0, 10); // mul r10, r0, r10
bad5731d 255 dirty_regb &= ~KRREG_P;
0e4d7ba5 256 hostreg_r[0] = -1;
d274c33b 257}
258
89fea1e9 259/* write dirty pr to host reg. Nothing is trashed */
260static void tr_flush_dirty_pr(int r)
261{
262 int ror = 0, reg;
6e39239f 263
89fea1e9 264 if (!(dirty_regb & (1 << (r+8)))) return;
265
266 switch (r&3) {
267 case 0: ror = 0; break;
268 case 1: ror = 24/2; break;
269 case 2: ror = 16/2; break;
270 }
271 reg = (r < 4) ? 8 : 9;
272 EOP_BIC_IMM(reg,reg,ror,0xff);
273 if (known_regs.r[r] != 0)
274 EOP_ORR_IMM(reg,reg,ror,known_regs.r[r]);
275 dirty_regb &= ~(1 << (r+8));
276}
277
278/* write all dirty pr0-pr7 to host regs. Nothing is trashed */
279static void tr_flush_dirty_prs(void)
5d817c91 280{
281 int i, ror = 0, reg;
bad5731d 282 int dirty = dirty_regb >> 8;
2385f273 283 if ((dirty&7) == 7) {
284 emit_mov_const(A_COND_AL, 8, known_regs.r[0]|(known_regs.r[1]<<8)|(known_regs.r[2]<<16));
285 dirty &= ~7;
286 }
287 if ((dirty&0x70) == 0x70) {
288 emit_mov_const(A_COND_AL, 9, known_regs.r[4]|(known_regs.r[5]<<8)|(known_regs.r[6]<<16));
289 dirty &= ~0x70;
290 }
5d817c91 291 /* r0-r7 */
bad5731d 292 for (i = 0; dirty && i < 8; i++, dirty >>= 1)
5d817c91 293 {
bad5731d 294 if (!(dirty&1)) continue;
5d817c91 295 switch (i&3) {
296 case 0: ror = 0; break;
297 case 1: ror = 24/2; break;
298 case 2: ror = 16/2; break;
299 }
300 reg = (i < 4) ? 8 : 9;
301 EOP_BIC_IMM(reg,reg,ror,0xff);
bad5731d 302 if (known_regs.r[i] != 0)
303 EOP_ORR_IMM(reg,reg,ror,known_regs.r[i]);
5d817c91 304 }
bad5731d 305 dirty_regb &= ~0xff00;
306}
307
89fea1e9 308/* write dirty pr and "forget" it. Nothing is trashed. */
309static void tr_release_pr(int r)
310{
311 tr_flush_dirty_pr(r);
312 known_regb &= ~(1 << (r+8));
313}
314
6e39239f 315/* fush ARM PSR to r6. Trashes r1 */
bad5731d 316static void tr_flush_dirty_ST(void)
317{
318 if (!(dirty_regb & KRREG_ST)) return;
319 EOP_BIC_IMM(6,6,0,0x0f);
6e39239f 320 EOP_MRS(1);
321 EOP_ORR_REG_LSR(6,6,1,28);
bad5731d 322 dirty_regb &= ~KRREG_ST;
6e39239f 323 hostreg_r[1] = -1;
324}
325
326/* inverse of above. Trashes r1 */
327static void tr_make_dirty_ST(void)
328{
329 if (dirty_regb & KRREG_ST) return;
330 if (known_regb & KRREG_ST) {
331 int flags = 0;
332 if (known_regs.gr[SSP_ST].h & SSP_FLAG_N) flags |= 8;
333 if (known_regs.gr[SSP_ST].h & SSP_FLAG_Z) flags |= 4;
334 EOP_MSR_IMM(4/2, flags);
335 } else {
336 EOP_MOV_REG_LSL(1, 6, 28);
337 EOP_MSR_REG(1);
338 hostreg_r[1] = -1;
339 }
340 dirty_regb |= KRREG_ST;
bad5731d 341}
342
343/* load 16bit val into host reg r0-r3. Nothing is trashed */
344static void tr_mov16(int r, int val)
345{
346 if (hostreg_r[r] != val) {
347 emit_mov_const(A_COND_AL, r, val);
348 hostreg_r[r] = val;
349 }
350}
351
352static void tr_mov16_cond(int cond, int r, int val)
353{
354 emit_mov_const(cond, r, val);
a6fb500b 355 hostreg_r[r] = -1;
5d817c91 356}
357
45883918 358/* trashes r1 */
ede7220f 359static void tr_flush_dirty_pmcrs(void)
360{
361 u32 i, val = (u32)-1;
d5276282 362 if (!(dirty_regb & 0x3ff80000)) return;
ede7220f 363
d5276282 364 if (dirty_regb & KRREG_PMC) {
365 val = known_regs.pmc.v;
e122fae6 366 emit_mov_const(A_COND_AL, 1, val);
367 EOP_STR_IMM(1,7,0x400+SSP_PMC*4);
ede7220f 368
d5276282 369 if (known_regs.emu_status & (SSP_PMC_SET|SSP_PMC_HAVE_ADDR)) {
2d2247c2 370 elprintf(EL_ANOMALY, "!! SSP_PMC_SET|SSP_PMC_HAVE_ADDR set on flush\n");
d5276282 371 tr_unhandled();
372 }
ede7220f 373 }
374 for (i = 0; i < 5; i++)
375 {
d5276282 376 if (dirty_regb & (1 << (20+i))) {
ede7220f 377 if (val != known_regs.pmac_read[i]) {
378 val = known_regs.pmac_read[i];
e122fae6 379 emit_mov_const(A_COND_AL, 1, val);
ede7220f 380 }
e122fae6 381 EOP_STR_IMM(1,7,0x454+i*4); // pmac_read
ede7220f 382 }
d5276282 383 if (dirty_regb & (1 << (25+i))) {
ede7220f 384 if (val != known_regs.pmac_write[i]) {
385 val = known_regs.pmac_write[i];
e122fae6 386 emit_mov_const(A_COND_AL, 1, val);
ede7220f 387 }
e122fae6 388 EOP_STR_IMM(1,7,0x46c+i*4); // pmac_write
ede7220f 389 }
390 }
d5276282 391 dirty_regb &= ~0x3ff80000;
e122fae6 392 hostreg_r[1] = -1;
ede7220f 393}
394
0e4d7ba5 395/* read bank word to r0 (upper bits zero). Thrashes r1. */
5d817c91 396static void tr_bank_read(int addr) /* word addr 0-0x1ff */
397{
bad5731d 398 int breg = 7;
399 if (addr > 0x7f) {
400 if (hostreg_r[1] != (0x100000|((addr&0x180)<<1))) {
401 EOP_ADD_IMM(1,7,30/2,(addr&0x180)>>1); // add r1, r7, ((op&0x180)<<1)
402 hostreg_r[1] = 0x100000|((addr&0x180)<<1);
5d817c91 403 }
bad5731d 404 breg = 1;
5d817c91 405 }
bad5731d 406 EOP_LDRH_IMM(0,breg,(addr&0x7f)<<1); // ldrh r0, [r1, (op&0x7f)<<1]
5d817c91 407 hostreg_r[0] = -1;
408}
409
410/* write r0 to bank. Trashes r1. */
411static void tr_bank_write(int addr)
412{
413 int breg = 7;
414 if (addr > 0x7f) {
d274c33b 415 if (hostreg_r[1] != (0x100000|((addr&0x180)<<1))) {
5d817c91 416 EOP_ADD_IMM(1,7,30/2,(addr&0x180)>>1); // add r1, r7, ((op&0x180)<<1)
d274c33b 417 hostreg_r[1] = 0x100000|((addr&0x180)<<1);
5d817c91 418 }
419 breg = 1;
420 }
b9c1d012 421 EOP_STRH_IMM(0,breg,(addr&0x7f)<<1); // strh r0, [r1, (op&0x7f)<<1]
5d817c91 422}
423
89fea1e9 424/* handle RAM bank pointer modifiers. if need_modulo, trash r1-r3, else nothing */
425static void tr_ptrr_mod(int r, int mod, int need_modulo, int count)
5d817c91 426{
a6fb500b 427 int modulo_shift = -1; /* unknown */
5d817c91 428
429 if (mod == 0) return;
430
431 if (!need_modulo || mod == 1) // +!
432 modulo_shift = 8;
bad5731d 433 else if (need_modulo && (known_regb & KRREG_ST)) {
434 modulo_shift = known_regs.gr[SSP_ST].h & 7;
5d817c91 435 if (modulo_shift == 0) modulo_shift = 8;
436 }
437
89fea1e9 438 if (modulo_shift == -1)
439 {
a6fb500b 440 int reg = (r < 4) ? 8 : 9;
89fea1e9 441 tr_release_pr(r);
0e4d7ba5 442 if (dirty_regb & KRREG_ST) {
443 // avoid flushing ARM flags
444 EOP_AND_IMM(1, 6, 0, 0x70);
445 EOP_SUB_IMM(1, 1, 0, 0x10);
446 EOP_AND_IMM(1, 1, 0, 0x70);
447 EOP_ADD_IMM(1, 1, 0, 0x10);
448 } else {
449 EOP_C_DOP_IMM(A_COND_AL,A_OP_AND,1,6,1,0,0x70); // ands r1, r6, #0x70
450 EOP_C_DOP_IMM(A_COND_EQ,A_OP_MOV,0,0,1,0,0x80); // moveq r1, #0x80
451 }
89fea1e9 452 EOP_MOV_REG_LSR(1, 1, 4); // mov r1, r1, lsr #4
453 EOP_RSB_IMM(2, 1, 0, 8); // rsb r1, r1, #8
454 EOP_MOV_IMM(3, 8/2, count); // mov r3, #0x01000000
455 if (r&3)
456 EOP_ADD_IMM(1, 1, 0, (r&3)*8); // add r1, r1, #(r&3)*8
457 EOP_MOV_REG2_ROR(reg,reg,1); // mov reg, reg, ror r1
458 if (mod == 2)
459 EOP_SUB_REG2_LSL(reg,reg,3,2); // sub reg, reg, #0x01000000 << r2
460 else EOP_ADD_REG2_LSL(reg,reg,3,2);
461 EOP_RSB_IMM(1, 1, 0, 32); // rsb r1, r1, #32
462 EOP_MOV_REG2_ROR(reg,reg,1); // mov reg, reg, ror r1
463 hostreg_r[1] = hostreg_r[2] = hostreg_r[3] = -1;
a6fb500b 464 }
465 else if (known_regb & (1 << (r + 8)))
466 {
467 int modulo = (1 << modulo_shift) - 1;
5d817c91 468 if (mod == 2)
89fea1e9 469 known_regs.r[r] = (known_regs.r[r] & ~modulo) | ((known_regs.r[r] - count) & modulo);
470 else known_regs.r[r] = (known_regs.r[r] & ~modulo) | ((known_regs.r[r] + count) & modulo);
a6fb500b 471 }
472 else
473 {
5d817c91 474 int reg = (r < 4) ? 8 : 9;
475 int ror = ((r&3) + 1)*8 - (8 - modulo_shift);
476 EOP_MOV_REG_ROR(reg,reg,ror);
477 // {add|sub} reg, reg, #1<<shift
89fea1e9 478 EOP_C_DOP_IMM(A_COND_AL,(mod==2)?A_OP_SUB:A_OP_ADD,0,reg,reg, 8/2, count << (8 - modulo_shift));
5d817c91 479 EOP_MOV_REG_ROR(reg,reg,32-ror);
480 }
481}
482
bad5731d 483/* handle writes r0 to (rX). Trashes r1.
484 * fortunately we can ignore modulo increment modes for writes. */
0e4d7ba5 485static void tr_rX_write(int op)
bad5731d 486{
487 if ((op&3) == 3)
488 {
489 int mod = (op>>2) & 3; // direct addressing
490 tr_bank_write((op & 0x100) + mod);
491 }
492 else
493 {
494 int r = (op&3) | ((op>>6)&4);
495 if (known_regb & (1 << (r + 8))) {
496 tr_bank_write((op&0x100) | known_regs.r[r]);
497 } else {
498 int reg = (r < 4) ? 8 : 9;
499 int ror = ((4 - (r&3))*8) & 0x1f;
500 EOP_AND_IMM(1,reg,ror/2,0xff); // and r1, r{7,8}, <mask>
501 if (r >= 4)
502 EOP_ORR_IMM(1,1,((ror-8)&0x1f)/2,1); // orr r1, r1, 1<<shift
503 if (r&3) EOP_ADD_REG_LSR(1,7,1, (r&3)*8-1); // add r1, r7, r1, lsr #lsr
504 else EOP_ADD_REG_LSL(1,7,1,1);
505 EOP_STRH_SIMPLE(0,1); // strh r0, [r1]
506 hostreg_r[1] = -1;
507 }
89fea1e9 508 tr_ptrr_mod(r, (op>>2) & 3, 0, 1);
509 }
510}
511
512/* read (rX) to r0. Trashes r1-r3. */
513static void tr_rX_read(int r, int mod)
514{
515 if ((r&3) == 3)
516 {
517 tr_bank_read(((r << 6) & 0x100) + mod); // direct addressing
518 }
519 else
520 {
521 if (known_regb & (1 << (r + 8))) {
6e39239f 522 tr_bank_read(((r << 6) & 0x100) | known_regs.r[r]);
89fea1e9 523 } else {
524 int reg = (r < 4) ? 8 : 9;
525 int ror = ((4 - (r&3))*8) & 0x1f;
526 EOP_AND_IMM(1,reg,ror/2,0xff); // and r1, r{7,8}, <mask>
527 if (r >= 4)
528 EOP_ORR_IMM(1,1,((ror-8)&0x1f)/2,1); // orr r1, r1, 1<<shift
529 if (r&3) EOP_ADD_REG_LSR(1,7,1, (r&3)*8-1); // add r1, r7, r1, lsr #lsr
530 else EOP_ADD_REG_LSL(1,7,1,1);
531 EOP_LDRH_SIMPLE(0,1); // ldrh r0, [r1]
0e4d7ba5 532 hostreg_r[0] = hostreg_r[1] = -1;
89fea1e9 533 }
534 tr_ptrr_mod(r, mod, 1, 1);
bad5731d 535 }
536}
537
0e4d7ba5 538/* read ((rX)) to r0. Trashes r1,r2. */
539static void tr_rX_read2(int op)
540{
541 int r = (op&3) | ((op>>6)&4); // src
542
543 if ((r&3) == 3) {
544 tr_bank_read((op&0x100) | ((op>>2)&3));
545 } else if (known_regb & (1 << (r+8))) {
546 tr_bank_read((op&0x100) | known_regs.r[r]);
547 } else {
548 int reg = (r < 4) ? 8 : 9;
549 int ror = ((4 - (r&3))*8) & 0x1f;
550 EOP_AND_IMM(1,reg,ror/2,0xff); // and r1, r{7,8}, <mask>
551 if (r >= 4)
552 EOP_ORR_IMM(1,1,((ror-8)&0x1f)/2,1); // orr r1, r1, 1<<shift
553 if (r&3) EOP_ADD_REG_LSR(1,7,1, (r&3)*8-1); // add r1, r7, r1, lsr #lsr
554 else EOP_ADD_REG_LSL(1,7,1,1);
555 EOP_LDRH_SIMPLE(0,1); // ldrh r0, [r1]
556 }
557 EOP_LDR_IMM(2,7,0x48c); // ptr_iram_rom
558 EOP_ADD_REG_LSL(2,2,0,1); // add r2, r2, r0, lsl #1
559 EOP_ADD_IMM(0,0,0,1); // add r0, r0, #1
560 if ((r&3) == 3) {
561 tr_bank_write((op&0x100) | ((op>>2)&3));
562 } else if (known_regb & (1 << (r+8))) {
563 tr_bank_write((op&0x100) | known_regs.r[r]);
564 } else {
565 EOP_STRH_SIMPLE(0,1); // strh r0, [r1]
566 hostreg_r[1] = -1;
567 }
568 EOP_LDRH_SIMPLE(0,2); // ldrh r0, [r2]
569 hostreg_r[0] = hostreg_r[2] = -1;
570}
89fea1e9 571
2385f273 572// check if AL is going to be used later in block
573static int tr_predict_al_need(void)
574{
575 int tmpv, tmpv2, op, pc = known_regs.gr[SSP_PC].h;
576
577 while (1)
578 {
579 op = PROGRAM(pc);
580 switch (op >> 9)
581 {
582 // ld d, s
583 case 0x00:
584 tmpv2 = (op >> 4) & 0xf; // dst
585 tmpv = op & 0xf; // src
586 if ((tmpv2 == SSP_A && tmpv == SSP_P) || tmpv2 == SSP_AL) // ld A, P; ld AL, *
587 return 0;
588 break;
589
590 // ld (ri), s
591 case 0x02:
592 // ld ri, s
593 case 0x0a:
594 // OP a, s
595 case 0x10: case 0x30: case 0x40: case 0x60: case 0x70:
596 tmpv = op & 0xf; // src
597 if (tmpv == SSP_AL) // OP *, AL
598 return 1;
599 break;
600
601 case 0x04:
602 case 0x06:
603 case 0x14:
604 case 0x34:
605 case 0x44:
606 case 0x64:
607 case 0x74: pc++; break;
608
609 // call cond, addr
610 case 0x24:
611 // bra cond, addr
612 case 0x26:
613 // mod cond, op
614 case 0x48:
615 // mpys?
616 case 0x1b:
617 // mpya (rj), (ri), b
618 case 0x4b: return 1;
619
620 // mld (rj), (ri), b
621 case 0x5b: return 0; // cleared anyway
622
623 // and A, *
624 case 0x50:
625 tmpv = op & 0xf; // src
626 if (tmpv == SSP_AL) return 1;
627 case 0x51: case 0x53: case 0x54: case 0x55: case 0x59: case 0x5c:
628 return 0;
629 }
630 pc++;
631 }
632}
633
634
bad5731d 635/* get ARM cond which would mean that SSP cond is satisfied. No trash. */
636static int tr_cond_check(int op)
637{
6e39239f 638 int f = (op & 0x100) >> 8;
bad5731d 639 switch (op&0xf0) {
640 case 0x00: return A_COND_AL; /* always true */
641 case 0x50: /* Z matches f(?) bit */
642 if (dirty_regb & KRREG_ST) return f ? A_COND_EQ : A_COND_NE;
643 EOP_TST_IMM(6, 0, 4);
644 return f ? A_COND_NE : A_COND_EQ;
645 case 0x70: /* N matches f(?) bit */
646 if (dirty_regb & KRREG_ST) return f ? A_COND_MI : A_COND_PL;
647 EOP_TST_IMM(6, 0, 8);
648 return f ? A_COND_NE : A_COND_EQ;
649 default:
2d2247c2 650 elprintf(EL_ANOMALY, "unimplemented cond?\n");
6e39239f 651 tr_unhandled();
bad5731d 652 return 0;
653 }
654}
655
656static int tr_neg_cond(int cond)
657{
658 switch (cond) {
2d2247c2 659 case A_COND_AL: elprintf(EL_ANOMALY, "neg for AL?\n"); exit(1);
bad5731d 660 case A_COND_EQ: return A_COND_NE;
661 case A_COND_NE: return A_COND_EQ;
662 case A_COND_MI: return A_COND_PL;
663 case A_COND_PL: return A_COND_MI;
2d2247c2 664 default: elprintf(EL_ANOMALY, "bad cond for neg\n"); exit(1);
bad5731d 665 }
666 return 0;
667}
668
ede7220f 669static int tr_aop_ssp2arm(int op)
670{
671 switch (op) {
672 case 1: return A_OP_SUB;
673 case 3: return A_OP_CMP;
674 case 4: return A_OP_ADD;
675 case 5: return A_OP_AND;
676 case 6: return A_OP_ORR;
677 case 7: return A_OP_EOR;
678 }
679
680 tr_unhandled();
681 return 0;
682}
683
684// -----------------------------------------------------
685
b9c1d012 686//@ r4: XXYY
687//@ r5: A
688//@ r6: STACK and emu flags
689//@ r7: SSP context
690//@ r10: P
691
bad5731d 692// read general reg to r0. Trashes r1
d5276282 693static void tr_GR0_to_r0(int op)
d274c33b 694{
695 tr_mov16(0, 0xffff);
696}
697
d5276282 698static void tr_X_to_r0(int op)
d274c33b 699{
700 if (hostreg_r[0] != (SSP_X<<16)) {
701 EOP_MOV_REG_LSR(0, 4, 16); // mov r0, r4, lsr #16
702 hostreg_r[0] = SSP_X<<16;
703 }
704}
705
d5276282 706static void tr_Y_to_r0(int op)
d274c33b 707{
d274c33b 708 if (hostreg_r[0] != (SSP_Y<<16)) {
709 EOP_MOV_REG_SIMPLE(0, 4); // mov r0, r4
710 hostreg_r[0] = SSP_Y<<16;
711 }
712}
713
d5276282 714static void tr_A_to_r0(int op)
d274c33b 715{
716 if (hostreg_r[0] != (SSP_A<<16)) {
717 EOP_MOV_REG_LSR(0, 5, 16); // mov r0, r5, lsr #16 @ AH
718 hostreg_r[0] = SSP_A<<16;
719 }
720}
721
d5276282 722static void tr_ST_to_r0(int op)
d274c33b 723{
724 // VR doesn't need much accuracy here..
725 EOP_MOV_REG_LSR(0, 6, 4); // mov r0, r6, lsr #4
726 EOP_AND_IMM(0, 0, 0, 0x67); // and r0, r0, #0x67
727 hostreg_r[0] = -1;
728}
729
d5276282 730static void tr_STACK_to_r0(int op)
d274c33b 731{
732 // 448
733 EOP_SUB_IMM(6, 6, 8/2, 0x20); // sub r6, r6, #1<<29
734 EOP_ADD_IMM(1, 7, 24/2, 0x04); // add r1, r7, 0x400
735 EOP_ADD_IMM(1, 1, 0, 0x48); // add r1, r1, 0x048
736 EOP_ADD_REG_LSR(1, 1, 6, 28); // add r1, r1, r6, lsr #28
737 EOP_LDRH_SIMPLE(0, 1); // ldrh r0, [r1]
738 hostreg_r[0] = hostreg_r[1] = -1;
739}
740
d5276282 741static void tr_PC_to_r0(int op)
d274c33b 742{
bad5731d 743 tr_mov16(0, known_regs.gr[SSP_PC].h);
d274c33b 744}
745
d5276282 746static void tr_P_to_r0(int op)
d274c33b 747{
748 tr_flush_dirty_P();
749 EOP_MOV_REG_LSR(0, 10, 16); // mov r0, r10, lsr #16
750 hostreg_r[0] = -1;
751}
d5276282 752
753static void tr_AL_to_r0(int op)
ede7220f 754{
d5276282 755 if (op == 0x000f) {
756 if (known_regb & KRREG_PMC) {
757 known_regs.emu_status &= ~(SSP_PMC_SET|SSP_PMC_HAVE_ADDR);
758 } else {
759 EOP_LDR_IMM(0,7,0x484); // ldr r1, [r7, #0x484] // emu_status
760 EOP_BIC_IMM(0,0,0,SSP_PMC_SET|SSP_PMC_HAVE_ADDR);
761 EOP_STR_IMM(0,7,0x484);
762 }
763 }
764
765 if (hostreg_r[0] != (SSP_AL<<16)) {
766 EOP_MOV_REG_SIMPLE(0, 5); // mov r0, r5
767 hostreg_r[0] = SSP_AL<<16;
768 }
ede7220f 769}
ede7220f 770
d5276282 771static void tr_PMX_to_r0(int reg)
ede7220f 772{
ede7220f 773 if ((known_regb & KRREG_PMC) && (known_regs.emu_status & SSP_PMC_SET))
774 {
d5276282 775 known_regs.pmac_read[reg] = known_regs.pmc.v;
ede7220f 776 known_regs.emu_status &= ~SSP_PMC_SET;
0336d643 777 known_regb |= 1 << (20+reg);
d5276282 778 dirty_regb |= 1 << (20+reg);
779 return;
ede7220f 780 }
781
d5276282 782 if ((known_regb & KRREG_PMC) && (known_regb & (1 << (20+reg))))
ede7220f 783 {
d5276282 784 u32 pmcv = known_regs.pmac_read[reg];
785 int mode = pmcv>>16;
786 known_regs.emu_status &= ~SSP_PMC_HAVE_ADDR;
787
ede7220f 788 if ((mode & 0xfff0) == 0x0800)
789 {
ede7220f 790 EOP_LDR_IMM(1,7,0x488); // rom_ptr
791 emit_mov_const(A_COND_AL, 0, (pmcv&0xfffff)<<1);
792 EOP_LDRH_REG(0,1,0); // ldrh r0, [r1, r0]
d5276282 793 known_regs.pmac_read[reg] += 1;
ede7220f 794 }
795 else if ((mode & 0x47ff) == 0x0018) // DRAM
796 {
797 int inc = get_inc(mode);
ede7220f 798 EOP_LDR_IMM(1,7,0x490); // dram_ptr
799 emit_mov_const(A_COND_AL, 0, (pmcv&0xffff)<<1);
800 EOP_LDRH_REG(0,1,0); // ldrh r0, [r1, r0]
d5276282 801 if (reg == 4 && (pmcv == 0x187f03 || pmcv == 0x187f04)) // wait loop detection
ede7220f 802 {
803 int flag = (pmcv == 0x187f03) ? SSP_WAIT_30FE06 : SSP_WAIT_30FE08;
804 tr_flush_dirty_ST();
805 EOP_LDR_IMM(1,7,0x484); // ldr r1, [r7, #0x484] // emu_status
806 EOP_TST_REG_SIMPLE(0,0);
71bb1b7b 807 EOP_C_DOP_IMM(A_COND_EQ,A_OP_SUB,0,11,11,22/2,1); // subeq r11, r11, #1024
d5276282 808 EOP_C_DOP_IMM(A_COND_EQ,A_OP_ORR,0, 1, 1,24/2,flag>>8); // orreq r1, r1, #SSP_WAIT_30FE08
ede7220f 809 EOP_STR_IMM(1,7,0x484); // str r1, [r7, #0x484] // emu_status
810 }
d5276282 811 known_regs.pmac_read[reg] += inc;
ede7220f 812 }
813 else
814 {
815 tr_unhandled();
816 }
d5276282 817 known_regs.pmc.v = known_regs.pmac_read[reg];
818 //known_regb |= KRREG_PMC;
819 dirty_regb |= KRREG_PMC;
820 dirty_regb |= 1 << (20+reg);
821 hostreg_r[0] = hostreg_r[1] = -1;
822 return;
823 }
ede7220f 824
d5276282 825 known_regb &= ~KRREG_PMC;
826 dirty_regb &= ~KRREG_PMC;
827 known_regb &= ~(1 << (20+reg));
828 dirty_regb &= ~(1 << (20+reg));
829
830 // call the C code to handle this
831 tr_flush_dirty_ST();
832 //tr_flush_dirty_pmcrs();
833 tr_mov16(0, reg);
45883918 834 emit_call(A_COND_AL, ssp_pm_read);
d5276282 835 hostreg_clear();
836}
837
838static void tr_PM0_to_r0(int op)
839{
840 tr_PMX_to_r0(0);
841}
842
843static void tr_PM1_to_r0(int op)
844{
845 tr_PMX_to_r0(1);
846}
847
848static void tr_PM2_to_r0(int op)
849{
850 tr_PMX_to_r0(2);
851}
852
853static void tr_XST_to_r0(int op)
854{
855 EOP_ADD_IMM(0, 7, 24/2, 4); // add r0, r7, #0x400
856 EOP_LDRH_IMM(0, 0, SSP_XST*4+2);
857}
858
859static void tr_PM4_to_r0(int op)
860{
861 tr_PMX_to_r0(4);
862}
863
864static void tr_PMC_to_r0(int op)
865{
866 if (known_regb & KRREG_PMC)
867 {
868 if (known_regs.emu_status & SSP_PMC_HAVE_ADDR) {
869 known_regs.emu_status |= SSP_PMC_SET;
870 known_regs.emu_status &= ~SSP_PMC_HAVE_ADDR;
871 // do nothing - this is handled elsewhere
872 } else {
873 tr_mov16(0, known_regs.pmc.l);
874 known_regs.emu_status |= SSP_PMC_HAVE_ADDR;
875 }
876 }
877 else
878 {
879 EOP_LDR_IMM(1,7,0x484); // ldr r1, [r7, #0x484] // emu_status
880 tr_flush_dirty_ST();
881 if (op != 0x000e)
882 EOP_LDR_IMM(0, 7, 0x400+SSP_PMC*4);
883 EOP_TST_IMM(1, 0, SSP_PMC_HAVE_ADDR);
884 EOP_C_DOP_IMM(A_COND_EQ,A_OP_ORR,0, 1, 1, 0, SSP_PMC_HAVE_ADDR); // orreq r1, r1, #..
885 EOP_C_DOP_IMM(A_COND_NE,A_OP_BIC,0, 1, 1, 0, SSP_PMC_HAVE_ADDR); // bicne r1, r1, #..
886 EOP_C_DOP_IMM(A_COND_NE,A_OP_ORR,0, 1, 1, 0, SSP_PMC_SET); // orrne r1, r1, #..
887 EOP_STR_IMM(1,7,0x484);
888 hostreg_r[0] = hostreg_r[1] = -1;
ede7220f 889 }
ede7220f 890}
891
d274c33b 892
d5276282 893typedef void (tr_read_func)(int op);
d274c33b 894
d5276282 895static tr_read_func *tr_read_funcs[16] =
d274c33b 896{
897 tr_GR0_to_r0,
898 tr_X_to_r0,
899 tr_Y_to_r0,
900 tr_A_to_r0,
901 tr_ST_to_r0,
902 tr_STACK_to_r0,
903 tr_PC_to_r0,
d5276282 904 tr_P_to_r0,
905 tr_PM0_to_r0,
906 tr_PM1_to_r0,
907 tr_PM2_to_r0,
908 tr_XST_to_r0,
909 tr_PM4_to_r0,
910 (tr_read_func *)tr_unhandled,
911 tr_PMC_to_r0,
912 tr_AL_to_r0
d274c33b 913};
914
915
b9c1d012 916// write r0 to general reg handlers. Trashes r1
6e39239f 917#define TR_WRITE_R0_TO_REG(reg) \
918{ \
919 hostreg_sspreg_changed(reg); \
920 hostreg_r[0] = (reg)<<16; \
921 if (const_val != -1) { \
922 known_regs.gr[reg].h = const_val; \
923 known_regb |= 1 << (reg); \
924 } else { \
925 known_regb &= ~(1 << (reg)); \
926 } \
b9c1d012 927}
928
6e39239f 929static void tr_r0_to_GR0(int const_val)
b9c1d012 930{
931 // do nothing
932}
933
6e39239f 934static void tr_r0_to_X(int const_val)
b9c1d012 935{
936 EOP_MOV_REG_LSL(4, 4, 16); // mov r4, r4, lsl #16
937 EOP_MOV_REG_LSR(4, 4, 16); // mov r4, r4, lsr #16
938 EOP_ORR_REG_LSL(4, 4, 0, 16); // orr r4, r4, r0, lsl #16
6e39239f 939 dirty_regb |= KRREG_P; // touching X or Y makes P dirty.
940 TR_WRITE_R0_TO_REG(SSP_X);
b9c1d012 941}
942
6e39239f 943static void tr_r0_to_Y(int const_val)
b9c1d012 944{
945 EOP_MOV_REG_LSR(4, 4, 16); // mov r4, r4, lsr #16
946 EOP_ORR_REG_LSL(4, 4, 0, 16); // orr r4, r4, r0, lsl #16
947 EOP_MOV_REG_ROR(4, 4, 16); // mov r4, r4, ror #16
bad5731d 948 dirty_regb |= KRREG_P;
6e39239f 949 TR_WRITE_R0_TO_REG(SSP_Y);
b9c1d012 950}
951
6e39239f 952static void tr_r0_to_A(int const_val)
b9c1d012 953{
2385f273 954 if (tr_predict_al_need()) {
955 EOP_MOV_REG_LSL(5, 5, 16); // mov r5, r5, lsl #16
956 EOP_MOV_REG_LSR(5, 5, 16); // mov r5, r5, lsr #16 @ AL
957 EOP_ORR_REG_LSL(5, 5, 0, 16); // orr r5, r5, r0, lsl #16
958 }
959 else
960 EOP_MOV_REG_LSL(5, 0, 16);
6e39239f 961 TR_WRITE_R0_TO_REG(SSP_A);
b9c1d012 962}
963
6e39239f 964static void tr_r0_to_ST(int const_val)
b9c1d012 965{
966 // VR doesn't need much accuracy here..
967 EOP_AND_IMM(1, 0, 0, 0x67); // and r1, r0, #0x67
968 EOP_AND_IMM(6, 6, 8/2, 0xe0); // and r6, r6, #7<<29 @ preserve STACK
969 EOP_ORR_REG_LSL(6, 6, 1, 4); // orr r6, r6, r1, lsl #4
6e39239f 970 TR_WRITE_R0_TO_REG(SSP_ST);
b9c1d012 971 hostreg_r[1] = -1;
6e39239f 972 dirty_regb &= ~KRREG_ST;
b9c1d012 973}
974
6e39239f 975static void tr_r0_to_STACK(int const_val)
b9c1d012 976{
977 // 448
978 EOP_ADD_IMM(1, 7, 24/2, 0x04); // add r1, r7, 0x400
979 EOP_ADD_IMM(1, 1, 0, 0x48); // add r1, r1, 0x048
d274c33b 980 EOP_ADD_REG_LSR(1, 1, 6, 28); // add r1, r1, r6, lsr #28
b9c1d012 981 EOP_STRH_SIMPLE(0, 1); // strh r0, [r1]
d274c33b 982 EOP_ADD_IMM(6, 6, 8/2, 0x20); // add r6, r6, #1<<29
b9c1d012 983 hostreg_r[1] = -1;
984}
985
6e39239f 986static void tr_r0_to_PC(int const_val)
b9c1d012 987{
45883918 988/*
989 * do nothing - dispatcher will take care of this
b9c1d012 990 EOP_MOV_REG_LSL(1, 0, 16); // mov r1, r0, lsl #16
d274c33b 991 EOP_STR_IMM(1,7,0x400+6*4); // str r1, [r7, #(0x400+6*8)]
b9c1d012 992 hostreg_r[1] = -1;
45883918 993*/
b9c1d012 994}
995
d5276282 996static void tr_r0_to_AL(int const_val)
997{
998 EOP_MOV_REG_LSR(5, 5, 16); // mov r5, r5, lsr #16
999 EOP_ORR_REG_LSL(5, 5, 0, 16); // orr r5, r5, r0, lsl #16
1000 EOP_MOV_REG_ROR(5, 5, 16); // mov r5, r5, ror #16
1001 hostreg_sspreg_changed(SSP_AL);
1002 if (const_val != -1) {
1003 known_regs.gr[SSP_A].l = const_val;
1004 known_regb |= 1 << SSP_AL;
1005 } else
1006 known_regb &= ~(1 << SSP_AL);
1007}
1008
1009static void tr_r0_to_PMX(int reg)
1010{
d5276282 1011 if ((known_regb & KRREG_PMC) && (known_regs.emu_status & SSP_PMC_SET))
1012 {
1013 known_regs.pmac_write[reg] = known_regs.pmc.v;
1014 known_regs.emu_status &= ~SSP_PMC_SET;
1015 known_regb |= 1 << (25+reg);
1016 dirty_regb |= 1 << (25+reg);
1017 return;
1018 }
0b5e8296 1019
d5276282 1020 if ((known_regb & KRREG_PMC) && (known_regb & (1 << (25+reg))))
1021 {
1022 int mode, addr;
1023
1024 known_regs.emu_status &= ~SSP_PMC_HAVE_ADDR;
1025
1026 mode = known_regs.pmac_write[reg]>>16;
1027 addr = known_regs.pmac_write[reg]&0xffff;
1028 if ((mode & 0x43ff) == 0x0018) // DRAM
1029 {
1030 int inc = get_inc(mode);
1031 if (mode & 0x0400) tr_unhandled();
1032 EOP_LDR_IMM(1,7,0x490); // dram_ptr
1033 emit_mov_const(A_COND_AL, 2, addr<<1);
1034 EOP_STRH_REG(0,1,2); // strh r0, [r1, r2]
1035 known_regs.pmac_write[reg] += inc;
1036 }
1037 else if ((mode & 0xfbff) == 0x4018) // DRAM, cell inc
1038 {
1039 if (mode & 0x0400) tr_unhandled();
1040 EOP_LDR_IMM(1,7,0x490); // dram_ptr
1041 emit_mov_const(A_COND_AL, 2, addr<<1);
1042 EOP_STRH_REG(0,1,2); // strh r0, [r1, r2]
1043 known_regs.pmac_write[reg] += (addr&1) ? 31 : 1;
1044 }
1045 else if ((mode & 0x47ff) == 0x001c) // IRAM
1046 {
1047 int inc = get_inc(mode);
1048 EOP_LDR_IMM(1,7,0x48c); // iram_ptr
1049 emit_mov_const(A_COND_AL, 2, (addr&0x3ff)<<1);
1050 EOP_STRH_REG(0,1,2); // strh r0, [r1, r2]
e122fae6 1051 EOP_MOV_IMM(1,0,1);
1052 EOP_STR_IMM(1,7,0x494); // iram_dirty
d5276282 1053 known_regs.pmac_write[reg] += inc;
1054 }
1055 else
1056 tr_unhandled();
1057
1058 known_regs.pmc.v = known_regs.pmac_write[reg];
1059 //known_regb |= KRREG_PMC;
1060 dirty_regb |= KRREG_PMC;
1061 dirty_regb |= 1 << (25+reg);
1062 hostreg_r[1] = hostreg_r[2] = -1;
e122fae6 1063 return;
d5276282 1064 }
1065
1066 known_regb &= ~KRREG_PMC;
1067 dirty_regb &= ~KRREG_PMC;
1068 known_regb &= ~(1 << (25+reg));
1069 dirty_regb &= ~(1 << (25+reg));
d5276282 1070
1071 // call the C code to handle this
1072 tr_flush_dirty_ST();
1073 //tr_flush_dirty_pmcrs();
1074 tr_mov16(1, reg);
45883918 1075 emit_call(A_COND_AL, ssp_pm_write);
d5276282 1076 hostreg_clear();
1077}
1078
1079static void tr_r0_to_PM0(int const_val)
1080{
1081 tr_r0_to_PMX(0);
1082}
1083
1084static void tr_r0_to_PM1(int const_val)
1085{
1086 tr_r0_to_PMX(1);
1087}
1088
1089static void tr_r0_to_PM2(int const_val)
1090{
1091 tr_r0_to_PMX(2);
1092}
1093
1094static void tr_r0_to_PM4(int const_val)
1095{
1096 tr_r0_to_PMX(4);
1097}
1098
1099static void tr_r0_to_PMC(int const_val)
1100{
1101 if ((known_regb & KRREG_PMC) && const_val != -1)
1102 {
1103 if (known_regs.emu_status & SSP_PMC_HAVE_ADDR) {
1104 known_regs.emu_status |= SSP_PMC_SET;
1105 known_regs.emu_status &= ~SSP_PMC_HAVE_ADDR;
1106 known_regs.pmc.h = const_val;
1107 } else {
1108 known_regs.emu_status |= SSP_PMC_HAVE_ADDR;
1109 known_regs.pmc.l = const_val;
1110 }
1111 }
1112 else
1113 {
1114 tr_flush_dirty_ST();
1115 if (known_regb & KRREG_PMC) {
1116 emit_mov_const(A_COND_AL, 1, known_regs.pmc.v);
1117 EOP_STR_IMM(1,7,0x400+SSP_PMC*4);
1118 known_regb &= ~KRREG_PMC;
1119 dirty_regb &= ~KRREG_PMC;
1120 }
1121 EOP_LDR_IMM(1,7,0x484); // ldr r1, [r7, #0x484] // emu_status
1122 EOP_ADD_IMM(2,7,24/2,4); // add r2, r7, #0x400
1123 EOP_TST_IMM(1, 0, SSP_PMC_HAVE_ADDR);
1124 EOP_C_AM3_IMM(A_COND_EQ,1,0,2,0,0,1,SSP_PMC*4); // strxx r0, [r2, #SSP_PMC]
1125 EOP_C_AM3_IMM(A_COND_NE,1,0,2,0,0,1,SSP_PMC*4+2);
1126 EOP_C_DOP_IMM(A_COND_EQ,A_OP_ORR,0, 1, 1, 0, SSP_PMC_HAVE_ADDR); // orreq r1, r1, #..
1127 EOP_C_DOP_IMM(A_COND_NE,A_OP_BIC,0, 1, 1, 0, SSP_PMC_HAVE_ADDR); // bicne r1, r1, #..
1128 EOP_C_DOP_IMM(A_COND_NE,A_OP_ORR,0, 1, 1, 0, SSP_PMC_SET); // orrne r1, r1, #..
1129 EOP_STR_IMM(1,7,0x484);
1130 hostreg_r[1] = hostreg_r[2] = -1;
1131 }
1132}
1133
6e39239f 1134typedef void (tr_write_func)(int const_val);
b9c1d012 1135
d5276282 1136static tr_write_func *tr_write_funcs[16] =
b9c1d012 1137{
1138 tr_r0_to_GR0,
1139 tr_r0_to_X,
1140 tr_r0_to_Y,
1141 tr_r0_to_A,
1142 tr_r0_to_ST,
1143 tr_r0_to_STACK,
1144 tr_r0_to_PC,
d5276282 1145 (tr_write_func *)tr_unhandled,
1146 tr_r0_to_PM0,
1147 tr_r0_to_PM1,
1148 tr_r0_to_PM2,
1149 (tr_write_func *)tr_unhandled,
1150 tr_r0_to_PM4,
1151 (tr_write_func *)tr_unhandled,
1152 tr_r0_to_PMC,
1153 tr_r0_to_AL
b9c1d012 1154};
1155
0e4d7ba5 1156static void tr_mac_load_XY(int op)
1157{
1158 tr_rX_read(op&3, (op>>2)&3); // X
1159 EOP_MOV_REG_LSL(4, 0, 16);
1160 tr_rX_read(((op>>4)&3)|4, (op>>6)&3); // Y
1161 EOP_ORR_REG_SIMPLE(4, 0);
1162 dirty_regb |= KRREG_P;
1163 hostreg_sspreg_changed(SSP_X);
1164 hostreg_sspreg_changed(SSP_Y);
1165 known_regb &= ~KRREG_X;
1166 known_regb &= ~KRREG_Y;
1167}
1168
ede7220f 1169// -----------------------------------------------------
1170
ede7220f 1171static int tr_detect_set_pm(unsigned int op, int *pc, int imm)
0e4d7ba5 1172{
ede7220f 1173 u32 pmcv, tmpv;
1174 if (!((op&0xfef0) == 0x08e0 && (PROGRAM(*pc)&0xfef0) == 0x08e0)) return 0;
1175
1176 // programming PMC:
1177 // ldi PMC, imm1
1178 // ldi PMC, imm2
1179 (*pc)++;
1180 pmcv = imm | (PROGRAM((*pc)++) << 16);
d5276282 1181 known_regs.pmc.v = pmcv;
ede7220f 1182 known_regb |= KRREG_PMC;
d5276282 1183 dirty_regb |= KRREG_PMC;
ede7220f 1184 known_regs.emu_status |= SSP_PMC_SET;
71bb1b7b 1185 n_in_ops++;
ede7220f 1186
1187 // check for possible reg programming
1188 tmpv = PROGRAM(*pc);
1189 if ((tmpv & 0xfff8) == 0x08 || (tmpv & 0xff8f) == 0x80)
1190 {
1191 int is_write = (tmpv & 0xff8f) == 0x80;
1192 int reg = is_write ? ((tmpv>>4)&0x7) : (tmpv&0x7);
1193 if (reg > 4) tr_unhandled();
d5276282 1194 if ((tmpv & 0x0f) != 0 && (tmpv & 0xf0) != 0) tr_unhandled();
ede7220f 1195 known_regs.pmac_read[is_write ? reg + 5 : reg] = pmcv;
1196 known_regb |= is_write ? (1 << (reg+25)) : (1 << (reg+20));
d5276282 1197 dirty_regb |= is_write ? (1 << (reg+25)) : (1 << (reg+20));
ede7220f 1198 known_regs.emu_status &= ~SSP_PMC_SET;
1199 (*pc)++;
71bb1b7b 1200 n_in_ops++;
ede7220f 1201 return 5;
0e4d7ba5 1202 }
1203
d5276282 1204 tr_unhandled();
ede7220f 1205 return 4;
1206}
1207
1208static const short pm0_block_seq[] = { 0x0880, 0, 0x0880, 0, 0x0840, 0x60 };
1209
1210static int tr_detect_pm0_block(unsigned int op, int *pc, int imm)
1211{
1212 // ldi ST, 0
1213 // ldi PM0, 0
1214 // ldi PM0, 0
1215 // ldi ST, 60h
1216 unsigned short *pp;
1217 if (op != 0x0840 || imm != 0) return 0;
1218 pp = PROGRAM_P(*pc);
1219 if (memcmp(pp, pm0_block_seq, sizeof(pm0_block_seq)) != 0) return 0;
1220
1221 EOP_AND_IMM(6, 6, 8/2, 0xe0); // and r6, r6, #7<<29 @ preserve STACK
1222 EOP_ORR_IMM(6, 6, 24/2, 6); // orr r6, r6, 0x600
1223 hostreg_sspreg_changed(SSP_ST);
1224 known_regs.gr[SSP_ST].h = 0x60;
1225 known_regb |= 1 << SSP_ST;
1226 dirty_regb &= ~KRREG_ST;
1227 (*pc) += 3*2;
71bb1b7b 1228 n_in_ops += 3;
ede7220f 1229 return 4*2;
0e4d7ba5 1230}
5d817c91 1231
d5276282 1232static int tr_detect_rotate(unsigned int op, int *pc, int imm)
1233{
1234 // @ 3DA2 and 426A
1235 // ld PMC, (r3|00)
1236 // ld (r3|00), PMC
1237 // ld -, AL
1238 if (op != 0x02e3 || PROGRAM(*pc) != 0x04e3 || PROGRAM(*pc + 1) != 0x000f) return 0;
1239
1240 tr_bank_read(0);
1241 EOP_MOV_REG_LSL(0, 0, 4);
1242 EOP_ORR_REG_LSR(0, 0, 0, 16);
1243 tr_bank_write(0);
1244 (*pc) += 2;
71bb1b7b 1245 n_in_ops += 2;
d5276282 1246 return 3;
1247}
1248
ede7220f 1249// -----------------------------------------------------
1250
45883918 1251static int translate_op(unsigned int op, int *pc, int imm, int *end_cond, int *jump_pc)
5d817c91 1252{
0e4d7ba5 1253 u32 tmpv, tmpv2, tmpv3;
5d817c91 1254 int ret = 0;
bad5731d 1255 known_regs.gr[SSP_PC].h = *pc;
5d817c91 1256
e807ac75 1257 switch (op >> 9)
1258 {
1259 // ld d, s
f48f5e3b 1260 case 0x00:
5d817c91 1261 if (op == 0) { ret++; break; } // nop
d274c33b 1262 tmpv = op & 0xf; // src
1263 tmpv2 = (op >> 4) & 0xf; // dst
d274c33b 1264 if (tmpv2 == SSP_A && tmpv == SSP_P) { // ld A, P
1265 tr_flush_dirty_P();
1266 EOP_MOV_REG_SIMPLE(5, 10);
d5276282 1267 hostreg_sspreg_changed(SSP_A);
bad5731d 1268 known_regb &= ~(KRREG_A|KRREG_AL);
d274c33b 1269 ret++; break;
1270 }
d5276282 1271 tr_read_funcs[tmpv](op);
6e39239f 1272 tr_write_funcs[tmpv2]((known_regb & (1 << tmpv)) ? known_regs.gr[tmpv].h : -1);
45883918 1273 if (tmpv2 == SSP_PC) {
1274 ret |= 0x10000;
1275 *end_cond = -A_COND_AL;
1276 }
bad5731d 1277 ret++; break;
1278
1279 // ld d, (ri)
89fea1e9 1280 case 0x01: {
89fea1e9 1281 int r = (op&3) | ((op>>6)&4);
1282 int mod = (op>>2)&3;
1283 tmpv = (op >> 4) & 0xf; // dst
d5276282 1284 ret = tr_detect_rotate(op, pc, imm);
1285 if (ret > 0) break;
89fea1e9 1286 if (tmpv != 0)
2385f273 1287 tr_rX_read(r, mod);
1288 else {
1289 int cnt = 1;
1290 while (PROGRAM(*pc) == op) {
1291 (*pc)++; cnt++; ret++;
1292 n_in_ops++;
1293 }
1294 tr_ptrr_mod(r, mod, 1, cnt); // skip
1295 }
6e39239f 1296 tr_write_funcs[tmpv](-1);
45883918 1297 if (tmpv == SSP_PC) {
1298 ret |= 0x10000;
1299 *end_cond = -A_COND_AL;
1300 }
89fea1e9 1301 ret++; break;
1302 }
bad5731d 1303
1304 // ld (ri), s
1305 case 0x02:
1306 tmpv = (op >> 4) & 0xf; // src
d5276282 1307 tr_read_funcs[tmpv](op);
0e4d7ba5 1308 tr_rX_write(op);
d274c33b 1309 ret++; break;
f48f5e3b 1310
1311 // ld a, adr
1312 case 0x03:
5d817c91 1313 tr_bank_read(op&0x1ff);
6e39239f 1314 tr_r0_to_A(-1);
5d817c91 1315 ret++; break;
1316
b9c1d012 1317 // ldi d, imm
1318 case 0x04:
ede7220f 1319 tmpv = (op & 0xf0) >> 4; // dst
1320 ret = tr_detect_pm0_block(op, pc, imm);
1321 if (ret > 0) break;
ede7220f 1322 ret = tr_detect_set_pm(op, pc, imm);
1323 if (ret > 0) break;
0b5e8296 1324 tr_mov16(0, imm);
1325 tr_write_funcs[tmpv](imm);
45883918 1326 if (tmpv == SSP_PC) {
1327 ret |= 0x10000;
1328 *jump_pc = imm;
1329 }
0b5e8296 1330 ret += 2; break;
b9c1d012 1331
bad5731d 1332 // ld d, ((ri))
0e4d7ba5 1333 case 0x05:
bad5731d 1334 tmpv2 = (op >> 4) & 0xf; // dst
0e4d7ba5 1335 tr_rX_read2(op);
6e39239f 1336 tr_write_funcs[tmpv2](-1);
45883918 1337 if (tmpv2 == SSP_PC) {
1338 ret |= 0x10000;
1339 *end_cond = -A_COND_AL;
1340 }
0e4d7ba5 1341 ret += 3; break;
b9c1d012 1342
5d817c91 1343 // ldi (ri), imm
1344 case 0x06:
5d817c91 1345 tr_mov16(0, imm);
0e4d7ba5 1346 tr_rX_write(op);
a6fb500b 1347 ret += 2; break;
f48f5e3b 1348
1349 // ld adr, a
1350 case 0x07:
d5276282 1351 tr_A_to_r0(op);
5d817c91 1352 tr_bank_write(op&0x1ff);
1353 ret++; break;
1354
d274c33b 1355 // ld d, ri
1356 case 0x09: {
bad5731d 1357 int r;
d274c33b 1358 r = (op&3) | ((op>>6)&4); // src
bad5731d 1359 tmpv2 = (op >> 4) & 0xf; // dst
bad5731d 1360 if ((r&3) == 3) tr_unhandled();
d274c33b 1361
bad5731d 1362 if (known_regb & (1 << (r+8))) {
1363 tr_mov16(0, known_regs.r[r]);
6e39239f 1364 tr_write_funcs[tmpv2](known_regs.r[r]);
d274c33b 1365 } else {
bad5731d 1366 int reg = (r < 4) ? 8 : 9;
d274c33b 1367 if (r&3) EOP_MOV_REG_LSR(0, reg, (r&3)*8); // mov r0, r{7,8}, lsr #lsr
1368 EOP_AND_IMM(0, (r&3)?0:reg, 0, 0xff); // and r0, r{7,8}, <mask>
1369 hostreg_r[0] = -1;
6e39239f 1370 tr_write_funcs[tmpv2](-1);
d274c33b 1371 }
d274c33b 1372 ret++; break;
1373 }
1374
bad5731d 1375 // ld ri, s
1376 case 0x0a: {
1377 int r;
1378 r = (op&3) | ((op>>6)&4); // dst
1379 tmpv = (op >> 4) & 0xf; // src
bad5731d 1380 if ((r&3) == 3) tr_unhandled();
1381
1382 if (known_regb & (1 << tmpv)) {
1383 known_regs.r[r] = known_regs.gr[tmpv].h;
1384 known_regb |= 1 << (r + 8);
1385 dirty_regb |= 1 << (r + 8);
1386 } else {
1387 int reg = (r < 4) ? 8 : 9;
1388 int ror = ((4 - (r&3))*8) & 0x1f;
d5276282 1389 tr_read_funcs[tmpv](op);
bad5731d 1390 EOP_BIC_IMM(reg, reg, ror/2, 0xff); // bic r{7,8}, r{7,8}, <mask>
1391 EOP_AND_IMM(0, 0, 0, 0xff); // and r0, r0, 0xff
1392 EOP_ORR_REG_LSL(reg, reg, 0, (r&3)*8); // orr r{7,8}, r{7,8}, r0, lsl #lsl
1393 hostreg_r[0] = -1;
1394 known_regb &= ~(1 << (r+8));
1395 dirty_regb &= ~(1 << (r+8));
1396 }
1397 ret++; break;
1398 }
1399
5d817c91 1400 // ldi ri, simm
67c81ee2 1401 case 0x0c: case 0x0d: case 0x0e: case 0x0f:
5d817c91 1402 tmpv = (op>>8)&7;
bad5731d 1403 known_regs.r[tmpv] = op;
1404 known_regb |= 1 << (tmpv + 8);
5d817c91 1405 dirty_regb |= 1 << (tmpv + 8);
1406 ret++; break;
bad5731d 1407
a6fb500b 1408 // call cond, addr
6e39239f 1409 case 0x24: {
1410 u32 *jump_op = NULL;
a6fb500b 1411 tmpv = tr_cond_check(op);
6e39239f 1412 if (tmpv != A_COND_AL) {
1413 jump_op = tcache_ptr;
1414 EOP_MOV_IMM(0, 0, 0); // placeholder for branch
1415 }
1416 tr_mov16(0, *pc);
1417 tr_r0_to_STACK(*pc);
1418 if (tmpv != A_COND_AL) {
1419 u32 *real_ptr = tcache_ptr;
1420 tcache_ptr = jump_op;
1421 EOP_C_B(tr_neg_cond(tmpv),0,real_ptr - jump_op - 2);
1422 tcache_ptr = real_ptr;
1423 }
a6fb500b 1424 tr_mov16_cond(tmpv, 0, imm);
45883918 1425 if (tmpv != A_COND_AL)
a6fb500b 1426 tr_mov16_cond(tr_neg_cond(tmpv), 0, *pc);
6e39239f 1427 tr_r0_to_PC(tmpv == A_COND_AL ? imm : -1);
ede7220f 1428 ret |= 0x10000;
45883918 1429 *end_cond = tmpv;
1430 *jump_pc = imm;
a6fb500b 1431 ret += 2; break;
6e39239f 1432 }
a6fb500b 1433
bad5731d 1434 // ld d, (a)
1435 case 0x25:
bad5731d 1436 tmpv2 = (op >> 4) & 0xf; // dst
d5276282 1437 tr_A_to_r0(op);
bad5731d 1438 EOP_LDR_IMM(1,7,0x48c); // ptr_iram_rom
1439 EOP_ADD_REG_LSL(0,1,0,1); // add r0, r1, r0, lsl #1
1440 EOP_LDRH_SIMPLE(0,0); // ldrh r0, [r0]
1441 hostreg_r[0] = hostreg_r[1] = -1;
6e39239f 1442 tr_write_funcs[tmpv2](-1);
45883918 1443 if (tmpv2 == SSP_PC) {
1444 ret |= 0x10000;
1445 *end_cond = -A_COND_AL;
1446 }
a6fb500b 1447 ret += 3; break;
bad5731d 1448
1449 // bra cond, addr
a6fb500b 1450 case 0x26:
bad5731d 1451 tmpv = tr_cond_check(op);
1452 tr_mov16_cond(tmpv, 0, imm);
45883918 1453 if (tmpv != A_COND_AL)
bad5731d 1454 tr_mov16_cond(tr_neg_cond(tmpv), 0, *pc);
6e39239f 1455 tr_r0_to_PC(tmpv == A_COND_AL ? imm : -1);
ede7220f 1456 ret |= 0x10000;
45883918 1457 *end_cond = tmpv;
1458 *jump_pc = imm;
a6fb500b 1459 ret += 2; break;
bad5731d 1460
89fea1e9 1461 // mod cond, op
89fea1e9 1462 case 0x48: {
1463 // check for repeats of this op
1464 tmpv = 1; // count
1465 while (PROGRAM(*pc) == op && (op & 7) != 6) {
1466 (*pc)++; tmpv++;
71bb1b7b 1467 n_in_ops++;
89fea1e9 1468 }
6e39239f 1469 if ((op&0xf0) != 0) // !always
1470 tr_make_dirty_ST();
1471
89fea1e9 1472 tmpv2 = tr_cond_check(op);
1473 switch (op & 7) {
1474 case 2: EOP_C_DOP_REG_XIMM(tmpv2,A_OP_MOV,1,0,5,tmpv,A_AM1_ASR,5); break; // shr (arithmetic)
1475 case 3: EOP_C_DOP_REG_XIMM(tmpv2,A_OP_MOV,1,0,5,tmpv,A_AM1_LSL,5); break; // shl
1476 case 6: EOP_C_DOP_IMM(tmpv2,A_OP_RSB,1,5,5,0,0); break; // neg
6e39239f 1477 case 7: EOP_C_DOP_REG_XIMM(tmpv2,A_OP_EOR,0,5,1,31,A_AM1_ASR,5); // eor r1, r5, r5, asr #31
1478 EOP_C_DOP_REG_XIMM(tmpv2,A_OP_ADD,1,1,5,31,A_AM1_LSR,5); // adds r5, r1, r5, lsr #31
89fea1e9 1479 hostreg_r[1] = -1; break; // abs
1480 default: tr_unhandled();
1481 }
6e39239f 1482
1483 hostreg_sspreg_changed(SSP_A);
1484 dirty_regb |= KRREG_ST;
1485 known_regb &= ~KRREG_ST;
1486 known_regb &= ~(KRREG_A|KRREG_AL);
89fea1e9 1487 ret += tmpv; break;
1488 }
0e4d7ba5 1489
bad5731d 1490 // mpys?
1491 case 0x1b:
0e4d7ba5 1492 tr_flush_dirty_P();
1493 tr_mac_load_XY(op);
1494 tr_make_dirty_ST();
1495 EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_SUB,1,5,5,0,A_AM1_LSL,10); // subs r5, r5, r10
1496 hostreg_sspreg_changed(SSP_A);
1497 known_regb &= ~(KRREG_A|KRREG_AL);
1498 dirty_regb |= KRREG_ST;
1499 ret++; break;
bad5731d 1500
1501 // mpya (rj), (ri), b
1502 case 0x4b:
0e4d7ba5 1503 tr_flush_dirty_P();
1504 tr_mac_load_XY(op);
1505 tr_make_dirty_ST();
1506 EOP_C_DOP_REG_XIMM(A_COND_AL,A_OP_ADD,1,5,5,0,A_AM1_LSL,10); // adds r5, r5, r10
1507 hostreg_sspreg_changed(SSP_A);
1508 known_regb &= ~(KRREG_A|KRREG_AL);
1509 dirty_regb |= KRREG_ST;
1510 ret++; break;
bad5731d 1511
1512 // mld (rj), (ri), b
1513 case 0x5b:
0e4d7ba5 1514 EOP_C_DOP_IMM(A_COND_AL,A_OP_MOV,1,0,5,0,0); // movs r5, #0
1515 hostreg_sspreg_changed(SSP_A);
1516 known_regs.gr[SSP_A].v = 0;
bad5731d 1517 known_regb |= (KRREG_A|KRREG_AL);
0e4d7ba5 1518 dirty_regb |= KRREG_ST;
1519 tr_mac_load_XY(op);
1520 ret++; break;
1521
1522 // OP a, s
1523 case 0x10:
1524 case 0x30:
1525 case 0x40:
1526 case 0x50:
1527 case 0x60:
1528 case 0x70:
1529 tmpv = op & 0xf; // src
1530 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1531 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
0e4d7ba5 1532 if (tmpv == SSP_P) {
1533 tr_flush_dirty_P();
1534 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3, 0,A_AM1_LSL,10); // OPs r5, r5, r10
1535 } else if (tmpv == SSP_A) {
1536 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3, 0,A_AM1_LSL, 5); // OPs r5, r5, r5
1537 } else {
d5276282 1538 tr_read_funcs[tmpv](op);
0e4d7ba5 1539 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL, 0); // OPs r5, r5, r0, lsl #16
1540 }
1541 hostreg_sspreg_changed(SSP_A);
1542 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1543 dirty_regb |= KRREG_ST;
1544 ret++; break;
1545
1546 // OP a, (ri)
1547 case 0x11:
1548 case 0x31:
1549 case 0x41:
1550 case 0x51:
1551 case 0x61:
1552 case 0x71:
1553 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1554 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1555 tr_rX_read((op&3)|((op>>6)&4), (op>>2)&3);
1556 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL,0); // OPs r5, r5, r0, lsl #16
1557 hostreg_sspreg_changed(SSP_A);
1558 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1559 dirty_regb |= KRREG_ST;
1560 ret++; break;
1561
1562 // OP a, adr
1563 case 0x13:
1564 case 0x33:
1565 case 0x43:
1566 case 0x53:
1567 case 0x63:
1568 case 0x73:
1569 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1570 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1571 tr_bank_read(op&0x1ff);
1572 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL,0); // OPs r5, r5, r0, lsl #16
1573 hostreg_sspreg_changed(SSP_A);
1574 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1575 dirty_regb |= KRREG_ST;
1576 ret++; break;
1577
1578 // OP a, imm
1579 case 0x14:
1580 case 0x34:
1581 case 0x44:
1582 case 0x54:
1583 case 0x64:
1584 case 0x74:
1585 tmpv = (op & 0xf0) >> 4;
1586 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1587 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1588 tr_mov16(0, imm);
1589 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL,0); // OPs r5, r5, r0, lsl #16
1590 hostreg_sspreg_changed(SSP_A);
1591 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1592 dirty_regb |= KRREG_ST;
1593 ret += 2; break;
1594
1595 // OP a, ((ri))
1596 case 0x15:
1597 case 0x35:
1598 case 0x45:
1599 case 0x55:
1600 case 0x65:
1601 case 0x75:
1602 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1603 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1604 tr_rX_read2(op);
1605 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL,0); // OPs r5, r5, r0, lsl #16
1606 hostreg_sspreg_changed(SSP_A);
1607 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1608 dirty_regb |= KRREG_ST;
1609 ret += 3; break;
1610
1611 // OP a, ri
1612 case 0x19:
1613 case 0x39:
1614 case 0x49:
1615 case 0x59:
1616 case 0x69:
1617 case 0x79: {
1618 int r;
1619 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1620 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1621 r = (op&3) | ((op>>6)&4); // src
1622 if ((r&3) == 3) tr_unhandled();
1623
1624 if (known_regb & (1 << (r+8))) {
1625 EOP_C_DOP_IMM(A_COND_AL,tmpv2,1,5,tmpv3,16/2,known_regs.r[r]); // OPs r5, r5, #val<<16
1626 } else {
1627 int reg = (r < 4) ? 8 : 9;
1628 if (r&3) EOP_MOV_REG_LSR(0, reg, (r&3)*8); // mov r0, r{7,8}, lsr #lsr
1629 EOP_AND_IMM(0, (r&3)?0:reg, 0, 0xff); // and r0, r{7,8}, <mask>
1630 EOP_C_DOP_REG_XIMM(A_COND_AL,tmpv2,1,5,tmpv3,16,A_AM1_LSL,0); // OPs r5, r5, r0, lsl #16
1631 hostreg_r[0] = -1;
1632 }
1633 hostreg_sspreg_changed(SSP_A);
1634 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1635 dirty_regb |= KRREG_ST;
1636 ret++; break;
1637 }
1638
1639 // OP simm
1640 case 0x1c:
1641 case 0x3c:
1642 case 0x4c:
1643 case 0x5c:
1644 case 0x6c:
1645 case 0x7c:
1646 tmpv2 = tr_aop_ssp2arm(op>>13); // op
1647 tmpv3 = (tmpv2 == A_OP_CMP) ? 0 : 5;
1648 EOP_C_DOP_IMM(A_COND_AL,tmpv2,1,5,tmpv3,16/2,op & 0xff); // OPs r5, r5, #val<<16
1649 hostreg_sspreg_changed(SSP_A);
1650 known_regb &= ~(KRREG_A|KRREG_AL|KRREG_ST);
1651 dirty_regb |= KRREG_ST;
bad5731d 1652 ret++; break;
e807ac75 1653 }
1654
71bb1b7b 1655 n_in_ops++;
1656
5d817c91 1657 return ret;
e807ac75 1658}
1659
45883918 1660static void emit_block_prologue(void)
1661{
1662 // check if there are enough cycles..
1663 // note: r0 must contain PC of current block
1664 EOP_CMP_IMM(11,0,0); // cmp r11, #0
1665 emit_call(A_COND_LE, ssp_drc_end);
1666}
1667
1668/* cond:
1669 * >0: direct (un)conditional jump
1670 * <0: indirect jump
1671 */
1672static void emit_block_epilogue(int cycles, int cond, int pc, int end_pc)
1673{
2d2247c2 1674 if (cycles > 0xff) { elprintf(EL_ANOMALY, "large cycle count: %i\n", cycles); cycles = 0xff; }
45883918 1675 EOP_SUB_IMM(11,11,0,cycles); // sub r11, r11, #cycles
1676
1677 if (cond < 0 || (end_pc >= 0x400 && pc < 0x400)) {
1678 // indirect jump, or rom -> iram jump, must use dispatcher
1679 emit_jump(A_COND_AL, ssp_drc_next);
1680 }
1681 else if (cond == A_COND_AL) {
1ca2ea4f 1682 u32 *target = (pc < 0x400) ? ssp_block_table_iram[ssp->drc.iram_context][pc] : ssp_block_table[pc];
45883918 1683 if (target != NULL)
1684 emit_jump(A_COND_AL, target);
1685 else {
1686 emit_jump(A_COND_AL, ssp_drc_next);
1687 // cause the next block to be emitted over jump instrction
1688 tcache_ptr--;
1689 }
1690 }
1691 else {
1ca2ea4f 1692 u32 *target1 = (pc < 0x400) ? ssp_block_table_iram[ssp->drc.iram_context][pc] : ssp_block_table[pc];
1693 u32 *target2 = (end_pc < 0x400) ? ssp_block_table_iram[ssp->drc.iram_context][end_pc] : ssp_block_table[end_pc];
45883918 1694 if (target1 != NULL)
1695 emit_jump(cond, target1);
1696 else emit_call(cond, ssp_drc_next_patch);
1697 if (target2 != NULL)
1698 emit_jump(tr_neg_cond(cond), target2); // neg_cond, to be able to swap jumps if needed
1699 else emit_call(tr_neg_cond(cond), ssp_drc_next_patch);
1700 }
1701}
1702
71bb1b7b 1703void *ssp_translate_block(int pc)
726bbb3e 1704{
e807ac75 1705 unsigned int op, op1, imm, ccount = 0;
5c129565 1706 unsigned int *block_start;
45883918 1707 int ret, end_cond = A_COND_AL, jump_pc = -1;
5c129565 1708
2d2247c2 1709 //printf("translate %04x -> %04x\n", pc<<1, (tcache_ptr-tcache)<<2);
5c129565 1710 block_start = tcache_ptr;
bad5731d 1711 known_regb = 0;
1712 dirty_regb = KRREG_P;
d5276282 1713 known_regs.emu_status = 0;
5d817c91 1714 hostreg_clear();
5c129565 1715
1716 emit_block_prologue();
726bbb3e 1717
e807ac75 1718 for (; ccount < 100;)
726bbb3e 1719 {
1720 op = PROGRAM(pc++);
1721 op1 = op >> 9;
e807ac75 1722 imm = (u32)-1;
5c129565 1723
e807ac75 1724 if ((op1 & 0xf) == 4 || (op1 & 0xf) == 6)
1725 imm = PROGRAM(pc++); // immediate
5c129565 1726
45883918 1727 ret = translate_op(op, &pc, imm, &end_cond, &jump_pc);
e807ac75 1728 if (ret <= 0)
1729 {
2d2247c2 1730 elprintf(EL_ANOMALY, "NULL func! op=%08x (%02x)\n", op, op1);
1731 //exit(1);
892b1dd2 1732 }
ede7220f 1733
45883918 1734 ccount += ret & 0xffff;
1735 if (ret & 0x10000) break;
726bbb3e 1736 }
5c129565 1737
45883918 1738 if (ccount >= 100) {
1739 end_cond = A_COND_AL;
1740 jump_pc = pc;
1741 emit_mov_const(A_COND_AL, 0, pc);
1742 }
0b5e8296 1743
89fea1e9 1744 tr_flush_dirty_prs();
1745 tr_flush_dirty_ST();
ede7220f 1746 tr_flush_dirty_pmcrs();
45883918 1747 emit_block_epilogue(ccount, end_cond, jump_pc, pc);
726bbb3e 1748
1ca2ea4f 1749 if (tcache_ptr - tcache > SSP_TCACHE_SIZE/4) {
2d2247c2 1750 elprintf(EL_ANOMALY, "tcache overflow!\n");
726bbb3e 1751 fflush(stdout);
1752 exit(1);
1753 }
1754
1755 // stats
1756 nblocks++;
2d2247c2 1757 //printf("%i blocks, %i bytes, k=%.3f\n", nblocks, (tcache_ptr - tcache)*4,
1758 // (double)(tcache_ptr - tcache) / (double)n_in_ops);
df143b36 1759
5d817c91 1760#ifdef DUMP_BLOCK
5c129565 1761 {
1762 FILE *f = fopen("tcache.bin", "wb");
1763 fwrite(tcache, 1, (tcache_ptr - tcache)*4, f);
1764 fclose(f);
1765 }
43e6eaad 1766 printf("dumped tcache.bin\n");
5c129565 1767 exit(0);
1768#endif
259ed0ea 1769
1770 handle_caches();
1771
5c129565 1772 return block_start;
726bbb3e 1773}
1774
1775
1776
1777// -----------------------------------------------------
1778
fad24893 1779static void ssp1601_state_load(void)
1780{
1781 ssp->drc.iram_dirty = 1;
1782 ssp->drc.iram_context = 0;
1783}
1784
e807ac75 1785int ssp1601_dyn_startup(void)
726bbb3e 1786{
1ca2ea4f 1787 memset(tcache, 0, SSP_TCACHE_SIZE);
1788 memset(ssp_block_table, 0, sizeof(ssp_block_table));
1789 memset(ssp_block_table_iram, 0, sizeof(ssp_block_table_iram));
e807ac75 1790 tcache_ptr = tcache;
726bbb3e 1791
fad24893 1792 PicoLoadStateHook = ssp1601_state_load;
1793
f5d1115f 1794 n_in_ops = 0;
d5276282 1795#ifdef ARM
1796 // hle'd blocks
1ca2ea4f 1797 ssp_block_table[0x800/2] = (void *) ssp_hle_800;
1798 ssp_block_table[0x902/2] = (void *) ssp_hle_902;
1799 ssp_block_table_iram[ 7][0x030/2] = (void *) ssp_hle_07_030;
1800 ssp_block_table_iram[ 7][0x036/2] = (void *) ssp_hle_07_036;
1801 ssp_block_table_iram[ 7][0x6d6/2] = (void *) ssp_hle_07_6d6;
1802 ssp_block_table_iram[11][0x12c/2] = (void *) ssp_hle_11_12c;
1803 ssp_block_table_iram[11][0x384/2] = (void *) ssp_hle_11_384;
1804 ssp_block_table_iram[11][0x38a/2] = (void *) ssp_hle_11_38a;
d5276282 1805#endif
1806
726bbb3e 1807 return 0;
1808}
1809
1810
1811void ssp1601_dyn_reset(ssp1601_t *ssp)
1812{
71bb1b7b 1813 ssp1601_reset(ssp);
1814 ssp->drc.iram_dirty = 1;
1815 ssp->drc.iram_context = 0;
1816 // must do this here because ssp is not available @ startup()
1817 ssp->drc.ptr_rom = (u32) Pico.rom;
1818 ssp->drc.ptr_iram_rom = (u32) svp->iram_rom;
1819 ssp->drc.ptr_dram = (u32) svp->dram;
1ca2ea4f 1820 ssp->drc.ptr_btable = (u32) ssp_block_table;
1821 ssp->drc.ptr_btable_iram = (u32) ssp_block_table_iram;
45883918 1822
1823 // prevent new versions of IRAM from appearing
1824 memset(svp->iram_rom, 0, 0x800);
726bbb3e 1825}
1826
726bbb3e 1827void ssp1601_dyn_run(int cycles)
1828{
b9c1d012 1829 if (ssp->emu_status & SSP_WAIT_MASK) return;
b9c1d012 1830
fad24893 1831#ifdef DUMP_BLOCK
1832 ssp_translate_block(DUMP_BLOCK >> 1);
1833#endif
1834#ifdef ARM
71bb1b7b 1835 ssp_drc_entry(cycles);
fad24893 1836#endif
726bbb3e 1837}
1838