32x: interpreter-wrap drc works (demos only). SVP drc refactoring.
[picodrive.git] / cpu / sh2 / compiler.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4
5 #include "sh2.h"
6 #include "compiler.h"
7 #include "../drc/cmn.h"
8
9 #define BLOCK_CYCLE_LIMIT 100
10
11 typedef enum {
12   SHR_R0 = 0, SHR_R15 = 15,
13   SHR_PC,  SHR_PPC, SHR_PR,   SHR_SR,
14   SHR_GBR, SHR_VBR, SHR_MACH, SHR_MACL,
15 } sh2_reg_e;
16
17 typedef struct block_desc_ {
18   u32 addr;                     // SH2 PC address
19   void *tcache_ptr;             // translated block for above PC
20   struct block_desc_ *next;     // next block with the same PC hash
21 } block_desc;
22
23 #define MAX_BLOCK_COUNT 1024
24 static block_desc *block_table;
25 static int block_count;
26
27 #define MAX_HASH_ENTRIES 1024
28 #define HASH_MASK (MAX_HASH_ENTRIES - 1)
29
30 #ifdef DRC_DEBUG
31 #include "mame/sh2dasm.h"
32 #include <platform/linux/host_dasm.h>
33 static void *tcache_dsm_ptr = tcache;
34 #endif
35
36 static void *tcache_ptr;
37
38 #include "../drc/emit_x86.c"
39
40 extern void sh2_drc_entry(SH2 *sh2, void *block);
41 extern void sh2_drc_exit(void);
42
43 // tmp
44 extern void __attribute__((regparm(2))) sh2_do_op(SH2 *sh2, int opcode);
45
46 static void *dr_find_block(block_desc *tab, u32 addr)
47 {
48   for (tab = tab->next; tab != NULL; tab = tab->next)
49     if (tab->addr == addr)
50       break;
51
52   if (tab != NULL)
53     return tab->tcache_ptr;
54
55   printf("block miss for %08x\n", addr);
56   return NULL;
57 }
58
59 static block_desc *dr_add_block(u32 addr, void *tcache_ptr)
60 {
61   block_desc *bd;
62
63   if (block_count == MAX_BLOCK_COUNT) {
64     // FIXME: flush cache instead
65     printf("block descriptor overflow\n");
66     exit(1);
67   }
68
69   bd = &block_table[block_count];
70   bd->addr = addr;
71   bd->tcache_ptr = tcache_ptr;
72   block_count++;
73
74   return bd;
75 }
76
77 #define HASH_FUNC(hash_tab, addr) \
78   ((block_desc **)(hash_tab))[(addr) & HASH_MASK]
79
80 // ---------------------------------------------------------------
81
82 static void emit_move_r_imm32(sh2_reg_e dst, u32 imm)
83 {
84   int host_dst = reg_map_g2h[dst];
85   int tmp = 0;
86
87   if (host_dst != -1)
88     tmp = host_dst;
89   emith_move_r_imm(tmp, imm);
90   if (host_dst == -1)
91     emith_ctx_write(tmp, dst * 4);
92 }
93
94 static void emit_move_r_r(sh2_reg_e dst, sh2_reg_e src)
95 {
96   int host_dst = reg_map_g2h[dst], host_src = reg_map_g2h[src];
97   int tmp = 0;
98
99   if (host_dst != -1 && host_src != -1) {
100     emith_move_r_r(host_dst, host_src);
101     return;
102   }
103
104   if (host_src != -1)
105     tmp = host_src;
106   if (host_dst != -1)
107     tmp = host_dst;
108
109   if (host_src == -1)
110     emith_ctx_read(tmp, src * 4);
111   if (host_dst == -1)
112     emith_ctx_write(tmp, dst * 4);
113 }
114
115 static void emit_braf(sh2_reg_e reg, u32 pc)
116 {
117   int host_reg = reg_map_g2h[reg];
118   if (host_reg == -1) {
119     emith_ctx_read(0, reg * 4);
120   } else
121     emith_move_r_r(0, host_reg);
122   emith_add_r_imm(0, pc);
123
124   emith_ctx_write(0, SHR_PC * 4);
125 }
126
127 // FIXME: this is broken, delayed insn shouldn't affect branch
128 #define DELAYED_OP \
129   if (delayed_op < 0) { \
130     delayed_op = op; \
131     goto next_op; \
132   } \
133   delayed_op = -1; \
134   pc -= 2 /* adjust back */
135
136 /*
137 static int sh2_translate_op4(int op)
138 {
139   switch (op & 0x000f)
140   {
141   case 0x0b:
142   default:
143     emith_pass_arg(2, sh2, op);
144     emith_call(sh2_do_op);
145     break;
146   }
147
148   return 0;
149 }
150 */
151
152 static void *sh2_translate(SH2 *sh2, block_desc *other_block)
153 {
154   void *block_entry = tcache_ptr;
155   block_desc *this_block;
156   unsigned int pc = sh2->pc;
157   int op, delayed_op = -1;
158   int cycles = 0;
159   u32 tmp;
160
161   this_block = dr_add_block(pc, block_entry);
162   if (other_block != NULL) {
163     printf("hash collision between %08x and %08x\n", pc, other_block->addr);
164     this_block->next = other_block;
165   }
166   HASH_FUNC(sh2->pc_hashtab, pc) = this_block;
167
168 #ifdef DRC_DEBUG
169   printf("== %csh2 block #%d %08x %p\n", sh2->is_slave ? 's' : 'm',
170     block_count, pc, block_entry);
171 #endif
172
173   while (cycles < BLOCK_CYCLE_LIMIT)
174   {
175     if (delayed_op >= 0)
176       op = delayed_op;
177     else {
178 next_op:
179       op = p32x_sh2_read16(pc, sh2->is_slave);
180
181 #ifdef DRC_DEBUG
182       {
183         char buff[64];
184         DasmSH2(buff, pc, op);
185         printf("%08x %04x %s\n", pc, op, buff);
186       }
187 #endif
188     }
189
190     pc += 2;
191     cycles++;
192
193     switch ((op >> 12) & 0x0f)
194     {
195     case 0x00:
196       // RTS        0000000000001011
197       if (op == 0x000b) {
198         DELAYED_OP;
199         emit_move_r_r(SHR_PC, SHR_PR);
200         cycles++;
201         goto end_block;
202       }
203       // RTE        0000000000101011
204       if (op == 0x002b) {
205         DELAYED_OP;
206         cycles++;
207         //emit_move_r_r(SHR_PC, SHR_PR);
208         emit_move_r_imm32(SHR_PC, pc - 4);
209         emith_pass_arg(2, sh2, op);
210         emith_call(sh2_do_op);
211         goto end_block;
212       }
213       // BRAF Rm    0000mmmm00100011
214       if (op == 0x0023) {
215         DELAYED_OP;
216         cycles++;
217         emit_braf((op >> 8) & 0x0f, pc);
218         goto end_block;
219       }
220       // BSRF Rm    0000mmmm00000011
221       if (op == 0x0003) {
222         DELAYED_OP;
223         emit_move_r_imm32(SHR_PR, pc);
224         emit_braf((op >> 8) & 0x0f, pc);
225         cycles++;
226         goto end_block;
227       }
228       goto default_;
229
230     case 0x04:
231       // JMP  @Rm   0100mmmm00101011
232       if ((op & 0xff) == 0x2b) {
233         DELAYED_OP;
234         emit_move_r_r(SHR_PC, (op >> 8) & 0x0f);
235         cycles++;
236         goto end_block;
237       }
238       // JSR  @Rm   0100mmmm00001011
239       if ((op & 0xff) == 0x0b) {
240         DELAYED_OP;
241         emit_move_r_imm32(SHR_PR, pc);
242         emit_move_r_r(SHR_PC, (op >> 8) & 0x0f);
243         cycles++;
244         goto end_block;
245       }
246       goto default_;
247
248     case 0x08: {
249       int adj = 2;
250       switch (op & 0x0f00) {
251       // BT/S label 10001101dddddddd
252       case 0x0d00:
253       // BF/S label 10001111dddddddd
254       case 0x0f00:
255         DELAYED_OP;
256         cycles--;
257         adj = 0;
258         // fallthrough
259       // BT   label 10001001dddddddd
260       case 0x0900:
261       // BF   label 10001011dddddddd
262       case 0x0b00:
263         cycles += 2;
264         emit_move_r_imm32(SHR_PC, pc);
265         emith_test_t();
266         tmp = ((signed int)(op << 24) >> 23);
267         EMIT_CONDITIONAL(emit_move_r_imm32(SHR_PC, pc + tmp + adj), (op & 0x0200) ? 1 : 0);
268         goto end_block;
269       }
270       goto default_;
271     }
272
273     case 0x0a:
274       // BRA  label 1010dddddddddddd
275       DELAYED_OP;
276     do_bra:
277       tmp = ((signed int)(op << 20) >> 19);
278       emit_move_r_imm32(SHR_PC, pc + tmp);
279       cycles++;
280       goto end_block;
281
282     case 0x0b:
283       // BSR  label 1011dddddddddddd
284       DELAYED_OP;
285       emit_move_r_imm32(SHR_PR, pc);
286       goto do_bra;
287
288     default:
289     default_:
290       emit_move_r_imm32(SHR_PC, pc - 2);
291       emith_pass_arg(2, sh2, op);
292       emith_call(sh2_do_op);
293       break;
294     }
295
296 #ifdef DRC_DEBUG
297     host_dasm(tcache_dsm_ptr, (char *)tcache_ptr - (char *)tcache_dsm_ptr);
298     tcache_dsm_ptr = tcache_ptr;
299 #endif
300   }
301
302 end_block:
303   if ((char *)tcache_ptr - (char *)tcache > DRC_TCACHE_SIZE) {
304     printf("tcache overflow!\n");
305     fflush(stdout);
306     exit(1);
307   }
308
309   if (reg_map_g2h[SHR_SR] == -1) {
310     emith_ctx_sub(cycles << 12, SHR_SR * 4);
311   } else
312     emith_sub_r_imm(reg_map_g2h[SHR_SR], cycles << 12);
313   emith_jump(sh2_drc_exit);
314
315 #ifdef DRC_DEBUG
316   host_dasm(tcache_dsm_ptr, (char *)tcache_ptr - (char *)tcache_dsm_ptr);
317   tcache_dsm_ptr = tcache_ptr;
318 #endif
319   return block_entry;
320
321 unimplemented:
322   // last op
323 #ifdef DRC_DEBUG
324   host_dasm(tcache_dsm_ptr, (char *)tcache_ptr - (char *)tcache_dsm_ptr);
325   tcache_dsm_ptr = tcache_ptr;
326 #endif
327   exit(1);
328 }
329
330 void __attribute__((noinline)) sh2_drc_dispatcher(SH2 *sh2)
331 {
332   while (((signed int)sh2->sr >> 12) > 0)
333   {
334     block_desc *bd = HASH_FUNC(sh2->pc_hashtab, sh2->pc);
335     void *block = NULL;
336
337     if (bd != NULL) {
338       if (bd->addr == sh2->pc)
339         block = bd->tcache_ptr;
340       else
341         block = dr_find_block(bd, sh2->pc);
342     }
343
344     if (block == NULL)
345       block = sh2_translate(sh2, bd);
346
347 #ifdef DRC_DEBUG
348     printf("= %csh2 enter %08x %p\n", sh2->is_slave ? 's' : 'm', sh2->pc, block);
349 #endif
350     sh2_drc_entry(sh2, block);
351   }
352 }
353
354 void sh2_execute(SH2 *sh2, int cycles)
355 {
356   sh2->cycles_aim += cycles;
357   cycles = sh2->cycles_aim - sh2->cycles_done;
358
359   // cycles are kept in SHR_SR unused bits (upper 20)
360   sh2->sr &= 0x3f3;
361   sh2->sr |= cycles << 12;
362   sh2_drc_dispatcher(sh2);
363
364   sh2->cycles_done += cycles - ((signed int)sh2->sr >> 12);
365 }
366
367
368 static int cmn_init_done;
369
370 static int common_init(void)
371 {
372   block_count = 0;
373   block_table = calloc(MAX_BLOCK_COUNT, sizeof(*block_table));
374   if (block_table == NULL)
375     return -1;
376
377   tcache_ptr = tcache;
378
379   cmn_init_done = 1;
380   return 0;
381 }
382
383 int sh2_drc_init(SH2 *sh2)
384 {
385   if (!cmn_init_done) {
386     int ret = common_init();
387     if (ret)
388       return ret;
389   }
390
391   assert(sh2->pc_hashtab == NULL);
392   sh2->pc_hashtab = calloc(sizeof(sh2->pc_hashtab[0]), MAX_HASH_ENTRIES);
393   if (sh2->pc_hashtab == NULL)
394     return -1;
395
396   return 0;
397 }
398