32x: memhandler improvements
[picodrive.git] / cpu / sh2 / sh2.c
1 /*
2  * PicoDrive
3  * (C) notaz, 2009,2010
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8 #include <string.h>
9 #include <stddef.h>
10
11 #include "sh2.h"
12 #include "../debug.h"
13 #include "compiler.h"
14
15 #define I 0xf0
16
17 int sh2_init(SH2 *sh2, int is_slave, SH2 *other_sh2)
18 {
19         int ret = 0;
20
21         memset(sh2, 0, offsetof(SH2, mult_m68k_to_sh2));
22         sh2->is_slave = is_slave;
23         sh2->other_sh2 = other_sh2;
24         pdb_register_cpu(sh2, PDBCT_SH2, is_slave ? "ssh2" : "msh2");
25 #ifdef DRC_SH2
26         ret = sh2_drc_init(sh2);
27 #endif
28         return ret;
29 }
30
31 void sh2_finish(SH2 *sh2)
32 {
33 #ifdef DRC_SH2
34         sh2_drc_finish(sh2);
35 #endif
36 }
37
38 void sh2_reset(SH2 *sh2)
39 {
40         sh2->pc = p32x_sh2_read32(0, sh2);
41         sh2->r[15] = p32x_sh2_read32(4, sh2);
42         sh2->sr = I;
43         sh2->vbr = 0;
44         sh2->pending_int_irq = 0;
45 }
46
47 void sh2_do_irq(SH2 *sh2, int level, int vector)
48 {
49         sh2->sr &= 0x3f3;
50
51         sh2->r[15] -= 4;
52         p32x_sh2_write32(sh2->r[15], sh2->sr, sh2);     /* push SR onto stack */
53         sh2->r[15] -= 4;
54         p32x_sh2_write32(sh2->r[15], sh2->pc, sh2);     /* push PC onto stack */
55
56         /* set I flags in SR */
57         sh2->sr = (sh2->sr & ~I) | (level << 4);
58
59         /* fetch PC */
60         sh2->pc = p32x_sh2_read32(sh2->vbr + vector * 4, sh2);
61
62         /* 13 cycles at best */
63         sh2->icount -= 13;
64 }
65
66 int sh2_irl_irq(SH2 *sh2, int level, int nested_call)
67 {
68         int taken;
69
70         sh2->pending_irl = level;
71         if (level < sh2->pending_int_irq)
72                 level = sh2->pending_int_irq;
73         sh2->pending_level = level;
74
75         taken = (level > ((sh2->sr >> 4) & 0x0f));
76         if (taken) {
77                 if (!nested_call) {
78                         // not in memhandler, so handle this now (recompiler friendly)
79                         // do this to avoid missing irqs that other SH2 might clear
80                         int vector = sh2->irq_callback(sh2, level);
81                         sh2_do_irq(sh2, level, vector);
82                         sh2->m68krcycles_done += C_SH2_TO_M68K(*sh2, 13);
83                 }
84                 else
85                         sh2->test_irq = 1;
86         }
87         return taken;
88 }
89
90 void sh2_internal_irq(SH2 *sh2, int level, int vector)
91 {
92         // FIXME: multiple internal irqs not handled..
93         // assuming internal irqs never clear until accepted
94         sh2->pending_int_irq = level;
95         sh2->pending_int_vector = vector;
96         if (level > sh2->pending_level)
97                 sh2->pending_level = level;
98
99         sh2->test_irq = 1;
100 }
101
102 #define SH2_REG_SIZE (offsetof(SH2, macl) + sizeof(sh2->macl))
103
104 void sh2_pack(const SH2 *sh2, unsigned char *buff)
105 {
106         unsigned int *p;
107
108         memcpy(buff, sh2, SH2_REG_SIZE);
109         p = (void *)(buff + SH2_REG_SIZE);
110
111         p[0] = sh2->pending_int_irq;
112         p[1] = sh2->pending_int_vector;
113 }
114
115 void sh2_unpack(SH2 *sh2, const unsigned char *buff)
116 {
117         unsigned int *p;
118
119         memcpy(sh2, buff, SH2_REG_SIZE);
120         p = (void *)(buff + SH2_REG_SIZE);
121
122         sh2->pending_int_irq = p[0];
123         sh2->pending_int_vector = p[1];
124         sh2->test_irq = 1;
125 }
126
127 #ifdef DRC_CMP
128
129 /* trace/compare */
130 #include <stdio.h>
131 #include <stdlib.h>
132 #include <pico/memory.h>
133 #undef _USE_CZ80 // HACK
134 #include <pico/pico_int.h>
135 #include <pico/debug.h>
136
137 static SH2 sh2ref[2];
138 static unsigned int mem_val;
139 static FILE *f;
140
141 enum ctl_byte {
142         CTL_MASTERSLAVE = 0x80,
143         CTL_EA = 0x82,
144         CTL_EAVAL = 0x83,
145         CTL_M68KPC = 0x84,
146         CTL_CYCLES = 0x85,
147 };
148
149 static unsigned int local_read32(SH2 *sh2, u32 a)
150 {
151         const sh2_memmap *sh2_map = sh2->read16_map;
152         u16 *pd;
153         uptr p;
154
155         sh2_map += (a >> 25);
156         p = sh2_map->addr;
157         if (!map_flag_set(p)) {
158                 pd = (u16 *)((p << 1) + ((a & sh2_map->mask) & ~1));
159                 return (pd[0] << 16) | pd[1];
160         }
161
162         if ((a & 0xfffff000) == 0xc0000000) {
163                 // data array
164                 pd = (u16 *)sh2->data_array + (a & 0xfff) / 2;
165                 return (pd[0] << 16) | pd[1];
166         }
167         if ((a & 0xdfffffc0) == 0x4000) {
168                 pd = &Pico32x.regs[(a & 0x3f) / 2];
169                 return (pd[0] << 16) | pd[1];
170         }
171         if ((a & 0xdffffe00) == 0x4200) {
172                 pd = &Pico32xMem->pal[(a & 0x1ff) / 2];
173                 return (pd[0] << 16) | pd[1];
174         }
175
176         return 0;
177 }
178
179 static void write_uint(unsigned char ctl, unsigned int v)
180 {
181         fwrite(&ctl, 1, 1, f);
182         fwrite(&v, sizeof(v), 1, f);
183 }
184
185 void do_sh2_trace(SH2 *current, int cycles)
186 {
187         static int current_slave = -1;
188         static u32 current_m68k_pc;
189         SH2 *sh2o = &sh2ref[current->is_slave];
190         u32 *regs_a = (void *)current;
191         u32 *regs_o = (void *)sh2o;
192         unsigned char v;
193         u32 val;
194         int i;
195
196         if (f == NULL)
197                 f = fopen("tracelog", "wb");
198
199         if (SekPc != current_m68k_pc) {
200                 current_m68k_pc = SekPc;
201                 write_uint(CTL_M68KPC, current_m68k_pc);
202         }
203
204         if (current->is_slave != current_slave) {
205                 current_slave = current->is_slave;
206                 v = CTL_MASTERSLAVE | current->is_slave;
207                 fwrite(&v, 1, 1, f);
208         }
209
210         for (i = 0; i < offsetof(SH2, read8_map) / 4; i++) {
211                 if (i == 17) // ppc
212                         continue;
213                 if (regs_a[i] != regs_o[i]) {
214                         write_uint(i, regs_a[i]);
215                         regs_o[i] = regs_a[i];
216                 }
217         }
218
219         if (current->ea != sh2o->ea) {
220                 write_uint(CTL_EA, current->ea);
221                 sh2o->ea = current->ea;
222         }
223         val = local_read32(current, current->ea);
224         if (mem_val != val) {
225                 write_uint(CTL_EAVAL, val);
226                 mem_val = val;
227         }
228         write_uint(CTL_CYCLES, cycles);
229 }
230
231 static const char *regnames[] = {
232         "r0",  "r1",  "r2",  "r3",
233         "r4",  "r5",  "r6",  "r7",
234         "r8",  "r9",  "r10", "r11",
235         "r12", "r13", "r14", "r15",
236         "pc",  "ppc", "pr",  "sr",
237         "gbr", "vbr", "mach","macl",
238 };
239
240 static void dump_regs(SH2 *sh2)
241 {
242         char csh2;
243         int i;
244
245         csh2 = sh2->is_slave ? 's' : 'm';
246         for (i = 0; i < 16/2; i++)
247                 printf("%csh2 r%d: %08x r%02d: %08x\n", csh2,
248                         i, sh2->r[i], i+8, sh2->r[i+8]);
249         printf("%csh2 PC: %08x  ,   %08x\n", csh2, sh2->pc, sh2->ppc);
250         printf("%csh2 SR:      %03x  PR: %08x\n", csh2, sh2->sr, sh2->pr);
251 }
252
253 void do_sh2_cmp(SH2 *current)
254 {
255         static int current_slave;
256         static u32 current_val;
257         SH2 *sh2o = &sh2ref[current->is_slave];
258         u32 *regs_a = (void *)current;
259         u32 *regs_o = (void *)sh2o;
260         unsigned char code;
261         int cycles_o = 666;
262         u32 sr, val;
263         int bad = 0;
264         int cycles;
265         int i, ret;
266
267         if (f == NULL) {
268                 f = fopen("tracelog", "rb");
269                 sh2ref[1].is_slave = 1;
270         }
271
272         while (1) {
273                 ret = fread(&code, 1, 1, f);
274                 if (ret <= 0)
275                         break;
276                 if (code == CTL_CYCLES) {
277                         fread(&cycles_o, 1, 4, f);
278                         break;
279                 }
280
281                 switch (code) {
282                 case CTL_MASTERSLAVE:
283                 case CTL_MASTERSLAVE + 1:
284                         current_slave = code & 1;
285                         break;
286                 case CTL_EA:
287                         fread(&sh2o->ea, 4, 1, f);
288                         break;
289                 case CTL_EAVAL:
290                         fread(&current_val, 4, 1, f);
291                         break;
292                 case CTL_M68KPC:
293                         fread(&val, 4, 1, f);
294                         if (SekPc != val) {
295                                 printf("m68k: %08x %08x\n", SekPc, val);
296                                 bad = 1;
297                         }
298                         break;
299                 default:
300                         if (code < offsetof(SH2, read8_map) / 4)
301                                 fread(regs_o + code, 4, 1, f);
302                         else {
303                                 printf("invalid code: %02x\n", code);
304                                 goto end;
305                         }
306                         break;
307                 }
308         }
309
310         if (ret <= 0) {
311                 printf("EOF?\n");
312                 goto end;
313         }
314
315         if (current->is_slave != current_slave) {
316                 printf("bad slave: %d %d\n", current->is_slave,
317                         current_slave);
318                 bad = 1;
319         }
320
321         for (i = 0; i < offsetof(SH2, read8_map) / 4; i++) {
322                 if (i == 17 || i == 19) // ppc, sr
323                         continue;
324                 if (regs_a[i] != regs_o[i]) {
325                         printf("bad %4s: %08x %08x\n",
326                                 regnames[i], regs_a[i], regs_o[i]);
327                         bad = 1;
328                 }
329         }
330
331         sr = current->sr & 0x3f3;
332         cycles = (signed int)current->sr >> 12;
333
334         if (sr != sh2o->sr) {
335                 printf("bad SR:  %03x %03x\n", sr, sh2o->sr);
336                 bad = 1;
337         }
338
339         if (cycles != cycles_o) {
340                 printf("bad cycles: %d %d\n", cycles, cycles_o);
341                 bad = 1;
342         }
343
344         val = local_read32(current, sh2o->ea);
345         if (val != current_val) {
346                 printf("bad val @%08x: %08x %08x\n", sh2o->ea, val, current_val);
347                 bad = 1;
348         }
349
350         if (!bad) {
351                 sh2o->ppc = current->pc;
352                 return;
353         }
354
355 end:
356         printf("--\n");
357         dump_regs(sh2o);
358         if (current->is_slave != current_slave)
359                 dump_regs(&sh2ref[current->is_slave ^ 1]);
360         PDebugDumpMem();
361         exit(1);
362 }
363
364 #endif // DRC_CMP