32x: implement VDP fill, improve PWM, division unit and scheduling
[picodrive.git] / pico / 32x / memory.c
... / ...
CommitLineData
1#include "../pico_int.h"
2#include "../memory.h"
3
4static const char str_mars[] = "MARS";
5
6struct Pico32xMem *Pico32xMem;
7
8static void bank_switch(int b);
9
10// poll detection
11#define POLL_THRESHOLD 6
12
13struct poll_det {
14 u32 addr, cycles, cyc_max;
15 int cnt, flag;
16};
17static struct poll_det m68k_poll, sh2_poll[2];
18
19static int p32x_poll_detect(struct poll_det *pd, u32 a, u32 cycles, int is_vdp)
20{
21 int ret = 0, flag = pd->flag;
22
23 if (is_vdp)
24 flag <<= 3;
25
26 if (a - 2 <= pd->addr && pd->addr <= a + 2 && cycles - pd->cycles < pd->cyc_max) {
27 pd->cnt++;
28 if (pd->cnt > POLL_THRESHOLD) {
29 if (!(Pico32x.emu_flags & flag)) {
30 elprintf(EL_32X, "%s poll addr %08x, cyc %u",
31 flag & (P32XF_68KPOLL|P32XF_68KVPOLL) ? "m68k" :
32 (flag & (P32XF_MSH2POLL|P32XF_MSH2VPOLL) ? "msh2" : "ssh2"), a, cycles - pd->cycles);
33 ret = 1;
34 }
35 Pico32x.emu_flags |= flag;
36 }
37 }
38 else
39 pd->cnt = 0;
40 pd->addr = a;
41 pd->cycles = cycles;
42
43 return ret;
44}
45
46static int p32x_poll_undetect(struct poll_det *pd, int is_vdp)
47{
48 int ret = 0, flag = pd->flag;
49 if (is_vdp)
50 flag <<= 3; // VDP only
51 else
52 flag |= flag << 3; // both
53 if (Pico32x.emu_flags & flag) {
54 elprintf(EL_32X, "poll %02x -> %02x", Pico32x.emu_flags, Pico32x.emu_flags & ~flag);
55 ret = 1;
56 }
57 Pico32x.emu_flags &= ~flag;
58 pd->addr = pd->cnt = 0;
59 return ret;
60}
61
62void p32x_poll_event(int cpu_mask, int is_vdp)
63{
64 if (cpu_mask & 1)
65 p32x_poll_undetect(&sh2_poll[0], is_vdp);
66 if (cpu_mask & 2)
67 p32x_poll_undetect(&sh2_poll[1], is_vdp);
68}
69
70// SH2 faking
71//#define FAKE_SH2
72int p32x_csum_faked;
73#ifdef FAKE_SH2
74static const u16 comm_fakevals[] = {
75 0x4d5f, 0x4f4b, // M_OK
76 0x535f, 0x4f4b, // S_OK
77 0x4D41, 0x5346, // MASF - Brutal Unleashed
78 0x5331, 0x4d31, // Darxide
79 0x5332, 0x4d32,
80 0x5333, 0x4d33,
81 0x0000, 0x0000, // eq for doom
82 0x0002, // Mortal Kombat
83// 0, // pad
84};
85
86static u32 sh2_comm_faker(u32 a)
87{
88 static int f = 0;
89 if (a == 0x28 && !p32x_csum_faked) {
90 p32x_csum_faked = 1;
91 return *(unsigned short *)(Pico.rom + 0x18e);
92 }
93 if (f >= sizeof(comm_fakevals) / sizeof(comm_fakevals[0]))
94 f = 0;
95 return comm_fakevals[f++];
96}
97#endif
98
99// DMAC handling
100static struct {
101 unsigned int sar0, dar0, tcr0; // src addr, dst addr, transfer count
102 unsigned int chcr0; // chan ctl
103 unsigned int sar1, dar1, tcr1; // same for chan 1
104 unsigned int chcr1;
105 int pad[4];
106 unsigned int dmaor;
107} * dmac0;
108
109static void dma_68k2sh2_do(void)
110{
111 unsigned short *dreqlen = &Pico32x.regs[0x10 / 2];
112 int i;
113
114 if (dmac0->tcr0 != *dreqlen)
115 elprintf(EL_32X|EL_ANOMALY, "tcr0 and dreq len differ: %d != %d", dmac0->tcr0, *dreqlen);
116
117 for (i = 0; i < Pico32x.dmac_ptr && dmac0->tcr0 > 0; i++) {
118 extern void p32x_sh2_write16(u32 a, u32 d, int id);
119 elprintf(EL_32X, "dmaw [%08x] %04x, left %d", dmac0->dar0, Pico32x.dmac_fifo[i], *dreqlen);
120 p32x_sh2_write16(dmac0->dar0, Pico32x.dmac_fifo[i], 0);
121 dmac0->dar0 += 2;
122 dmac0->tcr0--;
123 (*dreqlen)--;
124 }
125
126 Pico32x.dmac_ptr = 0; // HACK
127 Pico32x.regs[6 / 2] &= ~P32XS_FULL;
128 if (*dreqlen == 0)
129 Pico32x.regs[6 / 2] &= ~P32XS_68S; // transfer complete
130 if (dmac0->tcr0 == 0) {
131 dmac0->chcr0 |= 2; // DMA has ended normally
132 p32x_poll_undetect(&sh2_poll[0], 0);
133 }
134}
135
136// ------------------------------------------------------------------
137// 68k regs
138
139static u32 p32x_reg_read16(u32 a)
140{
141 a &= 0x3e;
142
143 if (a == 2) // INTM, INTS
144 return ((Pico32x.sh2irqi[0] & P32XI_CMD) >> 4) | ((Pico32x.sh2irqi[1] & P32XI_CMD) >> 3);
145#if 0
146 if ((a & 0x30) == 0x20)
147 return sh2_comm_faker(a);
148#else
149 if ((a & 0x30) == 0x20 && p32x_poll_detect(&m68k_poll, a, SekCyclesDoneT(), 0)) {
150 SekEndRun(16);
151 }
152#endif
153
154 if ((a & 0x30) == 0x30)
155 return p32x_pwm_read16(a);
156
157 return Pico32x.regs[a / 2];
158}
159
160static void p32x_reg_write8(u32 a, u32 d)
161{
162 u16 *r = Pico32x.regs;
163 a &= 0x3f;
164
165 // for things like bset on comm port
166 m68k_poll.cnt = 0;
167
168 if (a == 1 && !(r[0] & 1)) {
169 r[0] |= 1;
170 Pico32xStartup();
171 return;
172 }
173
174 if (!(r[0] & 1))
175 return;
176
177 switch (a) {
178 case 0: // adapter ctl
179 r[0] = (r[0] & 0x83) | ((d << 8) & P32XS_FM);
180 break;
181 case 3: // irq ctl
182 if ((d & 1) && !(Pico32x.sh2irqi[0] & P32XI_CMD)) {
183 Pico32x.sh2irqi[0] |= P32XI_CMD;
184 p32x_update_irls();
185 SekEndRun(16);
186 }
187 if ((d & 2) && !(Pico32x.sh2irqi[1] & P32XI_CMD)) {
188 Pico32x.sh2irqi[1] |= P32XI_CMD;
189 p32x_update_irls();
190 SekEndRun(16);
191 }
192 break;
193 case 5: // bank
194 d &= 7;
195 if (r[4 / 2] != d) {
196 r[4 / 2] = d;
197 bank_switch(d);
198 }
199 break;
200 case 7: // DREQ ctl
201 r[6 / 2] = (r[6 / 2] & P32XS_FULL) | (d & (P32XS_68S|P32XS_DMA|P32XS_RV));
202 break;
203 case 0x1b: // TV
204 r[0x1a / 2] = d;
205 break;
206 }
207}
208
209static void p32x_reg_write16(u32 a, u32 d)
210{
211 u16 *r = Pico32x.regs;
212 a &= 0x3e;
213
214 // for things like bset on comm port
215 m68k_poll.cnt = 0;
216
217 switch (a) {
218 case 0x00: // adapter ctl
219 r[0] = (r[0] & 0x83) | (d & P32XS_FM);
220 return;
221 case 0x10: // DREQ len
222 r[a / 2] = d & ~3;
223 return;
224 case 0x12: // FIFO reg
225 if (!(r[6 / 2] & P32XS_68S)) {
226 elprintf(EL_32X|EL_ANOMALY, "DREQ FIFO w16 without 68S?");
227 return;
228 }
229 if (Pico32x.dmac_ptr < DMAC_FIFO_LEN) {
230 Pico32x.dmac_fifo[Pico32x.dmac_ptr++] = d;
231 if ((Pico32x.dmac_ptr & 3) == 0 && (dmac0->chcr0 & 3) == 1 && (dmac0->dmaor & 1))
232 dma_68k2sh2_do();
233 if (Pico32x.dmac_ptr == DMAC_FIFO_LEN)
234 r[6 / 2] |= P32XS_FULL;
235 }
236 break;
237 }
238
239 // DREQ src, dst
240 if ((a & 0x38) == 0x08) {
241 r[a / 2] = d;
242 return;
243 }
244 // comm port
245 else if ((a & 0x30) == 0x20 && r[a / 2] != d) {
246 r[a / 2] = d;
247 if (p32x_poll_undetect(&sh2_poll[0], 0) || p32x_poll_undetect(&sh2_poll[1], 0))
248 // if some SH2 is busy waiting, it needs to see the result ASAP
249 SekEndRun(16);
250 return;
251 }
252 // PWM
253 else if ((a & 0x30) == 0x30) {
254 p32x_pwm_write16(a, d);
255 return;
256 }
257
258 p32x_reg_write8(a + 1, d);
259}
260
261// ------------------------------------------------------------------
262// VDP regs
263static u32 p32x_vdp_read16(u32 a)
264{
265 a &= 0x0e;
266
267 return Pico32x.vdp_regs[a / 2];
268}
269
270static void p32x_vdp_write8(u32 a, u32 d)
271{
272 u16 *r = Pico32x.vdp_regs;
273 a &= 0x0f;
274
275 // for FEN checks between writes
276 sh2_poll[0].cnt = 0;
277
278 // TODO: verify what's writeable
279 switch (a) {
280 case 0x01:
281 // priority inversion is handled in palette
282 if ((r[0] ^ d) & P32XV_PRI)
283 Pico32x.dirty_pal = 1;
284 r[0] = (r[0] & P32XV_nPAL) | (d & 0xff);
285 if ((d & 3) == 3)
286 elprintf(EL_32X|EL_ANOMALY, "TODO: mode3");
287 break;
288 case 0x05: // fill len
289 r[4 / 2] = d & 0xff;
290 break;
291 case 0x0b:
292 d &= 1;
293 Pico32x.pending_fb = d;
294 // if we are blanking and FS bit is changing
295 if (((r[0x0a/2] & P32XV_VBLK) || (r[0] & P32XV_Mx) == 0) && ((r[0x0a/2] ^ d) & P32XV_FS)) {
296 r[0x0a/2] ^= 1;
297 Pico32xSwapDRAM(d ^ 1);
298 elprintf(EL_32X, "VDP FS: %d", r[0x0a/2] & P32XV_FS);
299 }
300 break;
301 }
302}
303
304static void p32x_vdp_write16(u32 a, u32 d)
305{
306 a &= 0x0e;
307 if (a == 6) { // fill start
308 Pico32x.vdp_regs[6 / 2] = d;
309 return;
310 }
311 if (a == 8) { // fill data
312 u16 *dram = Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1];
313 int len = Pico32x.vdp_regs[4 / 2];
314 a = Pico32x.vdp_regs[6 / 2];
315 while (len--) {
316 dram[a] = d;
317 a = (a & 0xff00) | ((a + 1) & 0xff);
318 }
319 Pico32x.vdp_regs[6 / 2] = a;
320 Pico32x.vdp_regs[8 / 2] = d;
321 return;
322 }
323
324 p32x_vdp_write8(a | 1, d);
325}
326
327// ------------------------------------------------------------------
328// SH2 regs
329
330static u32 p32x_sh2reg_read16(u32 a, int cpuid)
331{
332 u16 *r = Pico32x.regs;
333 a &= 0xfe; // ?
334
335 switch (a) {
336 case 0x00: // adapter/irq ctl
337 return (r[0] & P32XS_FM) | Pico32x.sh2_regs[0] | Pico32x.sh2irq_mask[cpuid];
338 case 0x04: // H count
339 return Pico32x.sh2_regs[4 / 2];
340 case 0x10: // DREQ len
341 return r[a / 2];
342 }
343
344 // DREQ src, dst
345 if ((a & 0x38) == 0x08)
346 return r[a / 2];
347 // comm port
348 if ((a & 0x30) == 0x20) {
349 if (p32x_poll_detect(&sh2_poll[cpuid], a, ash2_cycles_done(), 0))
350 ash2_end_run(8);
351 return r[a / 2];
352 }
353 if ((a & 0x30) == 0x30) {
354 sh2_poll[cpuid].cnt = 0;
355 return p32x_pwm_read16(a);
356 }
357
358 return 0;
359}
360
361static void p32x_sh2reg_write8(u32 a, u32 d, int cpuid)
362{
363 a &= 0xff;
364 switch (a) {
365 case 0: // FM
366 Pico32x.regs[0] &= ~P32XS_FM;
367 Pico32x.regs[0] |= (d << 8) & P32XS_FM;
368 break;
369 case 1: //
370 Pico32x.sh2irq_mask[cpuid] = d & 0x8f;
371 Pico32x.sh2_regs[0] &= ~0x80;
372 Pico32x.sh2_regs[0] |= d & 0x80;
373 p32x_update_irls();
374 break;
375 case 5: // H count
376 Pico32x.sh2_regs[4 / 2] = d & 0xff;
377 break;
378 }
379}
380
381static void p32x_sh2reg_write16(u32 a, u32 d, int cpuid)
382{
383 a &= 0xfe;
384
385 // comm
386 if ((a & 0x30) == 0x20 && Pico32x.regs[a/2] != d) {
387 Pico32x.regs[a / 2] = d;
388 p32x_poll_undetect(&m68k_poll, 0);
389 p32x_poll_undetect(&sh2_poll[cpuid ^ 1], 0);
390 return;
391 }
392 // PWM
393 else if ((a & 0x30) == 0x30) {
394 p32x_pwm_write16(a, d);
395 return;
396 }
397
398 switch (a) {
399 case 0: // FM
400 Pico32x.regs[0] &= ~P32XS_FM;
401 Pico32x.regs[0] |= d & P32XS_FM;
402 break;
403 case 0x14: Pico32x.sh2irqs &= ~P32XI_VRES; goto irls;
404 case 0x16: Pico32x.sh2irqs &= ~P32XI_VINT; goto irls;
405 case 0x18: Pico32x.sh2irqs &= ~P32XI_HINT; goto irls;
406 case 0x1a: Pico32x.sh2irqi[cpuid] &= ~P32XI_CMD; goto irls;
407 case 0x1c:
408 Pico32x.sh2irqs &= ~P32XI_PWM;
409 p32x_pwm_irq_check(0);
410 goto irls;
411 }
412
413 p32x_sh2reg_write8(a | 1, d, cpuid);
414 return;
415
416irls:
417 p32x_update_irls();
418}
419
420// ------------------------------------------------------------------
421// SH2 internal peripherals
422static u32 sh2_peripheral_read8(u32 a, int id)
423{
424 u8 *r = (void *)Pico32xMem->sh2_peri_regs[id];
425 u32 d;
426
427 a &= 0x1ff;
428 d = r[a];
429 if (a == 4)
430 d = 0x84; // SCI SSR
431
432 elprintf(EL_32X, "%csh2 peri r8 [%08x] %02x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id));
433 return d;
434}
435
436static u32 sh2_peripheral_read32(u32 a, int id)
437{
438 u32 d;
439 a &= 0x1fc;
440 d = Pico32xMem->sh2_peri_regs[id][a / 4];
441
442 elprintf(EL_32X, "%csh2 peri r32 [%08x] %08x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id));
443 return d;
444}
445
446static void sh2_peripheral_write8(u32 a, u32 d, int id)
447{
448 u8 *r = (void *)Pico32xMem->sh2_peri_regs[id];
449 elprintf(EL_32X, "%csh2 peri w8 [%08x] %02x @%06x", id ? 's' : 'm', a, d, sh2_pc(id));
450
451 a &= 0x1ff;
452 r[a] = d;
453}
454
455static void sh2_peripheral_write32(u32 a, u32 d, int id)
456{
457 u32 *r = Pico32xMem->sh2_peri_regs[id];
458 elprintf(EL_32X, "%csh2 peri w32 [%08x] %08x @%06x", id ? 's' : 'm', a, d, sh2_pc(id));
459
460 a &= 0x1fc;
461 r[a / 4] = d;
462
463 switch (a) {
464 // division unit (TODO: verify):
465 case 0x104: // DVDNT: divident L, starts divide
466 elprintf(EL_32X, "%csh2 divide %08x / %08x", id ? 's' : 'm', d, r[0x100 / 4]);
467 if (r[0x100 / 4]) {
468 signed int divisor = r[0x100 / 4];
469 r[0x118 / 4] = r[0x110 / 4] = (signed int)d % divisor;
470 r[0x104 / 4] = r[0x11c / 4] = r[0x114 / 4] = (signed int)d / divisor;
471 }
472 break;
473 case 0x114:
474 elprintf(EL_32X, "%csh2 divide %08x%08x / %08x @%08x",
475 id ? 's' : 'm', r[0x110 / 4], d, r[0x100 / 4], sh2_pc(id));
476 if (r[0x100 / 4]) {
477 signed long long divident = (signed long long)r[0x110 / 4] << 32 | d;
478 signed int divisor = r[0x100 / 4];
479 // XXX: undocumented mirroring to 0x118,0x11c?
480 r[0x118 / 4] = r[0x110 / 4] = divident % divisor;
481 r[0x11c / 4] = r[0x114 / 4] = divident / divisor;
482 }
483 break;
484 }
485
486 if ((a == 0x1b0 || a == 0x18c) && (dmac0->chcr0 & 3) == 1 && (dmac0->dmaor & 1)) {
487 elprintf(EL_32X, "sh2 DMA %08x -> %08x, cnt %d, chcr %04x @%06x",
488 dmac0->sar0, dmac0->dar0, dmac0->tcr0, dmac0->chcr0, sh2_pc(id));
489 dmac0->tcr0 &= 0xffffff;
490
491 // HACK: assume bus is busy and SH2 is halted
492 // XXX: use different mechanism for this, not poll det
493 Pico32x.emu_flags |= id ? P32XF_SSH2POLL : P32XF_MSH2POLL;
494 ash2_end_run(5);
495
496 // DREQ is only sent after first 4 words are written.
497 // we do multiple of 4 words to avoid messing up alignment
498 if (dmac0->sar0 == 0x20004012 && Pico32x.dmac_ptr && (Pico32x.dmac_ptr & 3) == 0) {
499 elprintf(EL_32X, "68k -> sh2 DMA");
500 dma_68k2sh2_do();
501 }
502 }
503}
504
505// ------------------------------------------------------------------
506// default 32x handlers
507u32 PicoRead8_32x(u32 a)
508{
509 u32 d = 0;
510 if ((a & 0xffc0) == 0x5100) { // a15100
511 d = p32x_reg_read16(a);
512 goto out_16to8;
513 }
514
515 if (!(Pico32x.regs[0] & 1))
516 goto no_vdp;
517
518 if ((a & 0xfff0) == 0x5180) { // a15180
519 d = p32x_vdp_read16(a);
520 goto out_16to8;
521 }
522
523 if ((a & 0xfe00) == 0x5200) { // a15200
524 d = Pico32xMem->pal[(a & 0x1ff) / 2];
525 goto out_16to8;
526 }
527
528no_vdp:
529 if ((a & 0xfffc) == 0x30ec) { // a130ec
530 d = str_mars[a & 3];
531 goto out;
532 }
533
534 elprintf(EL_UIO, "m68k unmapped r8 [%06x] @%06x", a, SekPc);
535 return d;
536
537out_16to8:
538 if (a & 1)
539 d &= 0xff;
540 else
541 d >>= 8;
542
543out:
544 elprintf(EL_32X, "m68k 32x r8 [%06x] %02x @%06x", a, d, SekPc);
545 return d;
546}
547
548u32 PicoRead16_32x(u32 a)
549{
550 u32 d = 0;
551 if ((a & 0xffc0) == 0x5100) { // a15100
552 d = p32x_reg_read16(a);
553 goto out;
554 }
555
556 if (!(Pico32x.regs[0] & 1))
557 goto no_vdp;
558
559 if ((a & 0xfff0) == 0x5180) { // a15180
560 d = p32x_vdp_read16(a);
561 goto out;
562 }
563
564 if ((a & 0xfe00) == 0x5200) { // a15200
565 d = Pico32xMem->pal[(a & 0x1ff) / 2];
566 goto out;
567 }
568
569no_vdp:
570 if ((a & 0xfffc) == 0x30ec) { // a130ec
571 d = !(a & 2) ? ('M'<<8)|'A' : ('R'<<8)|'S';
572 goto out;
573 }
574
575 elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc);
576 return d;
577
578out:
579 elprintf(EL_32X, "m68k 32x r16 [%06x] %04x @%06x", a, d, SekPc);
580 return d;
581}
582
583void PicoWrite8_32x(u32 a, u32 d)
584{
585 if ((a & 0xfc00) == 0x5000)
586 elprintf(EL_32X, "m68k 32x w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
587
588 if ((a & 0xffc0) == 0x5100) { // a15100
589 p32x_reg_write8(a, d);
590 return;
591 }
592
593 if (!(Pico32x.regs[0] & 1))
594 goto no_vdp;
595
596 if ((a & 0xfff0) == 0x5180) { // a15180
597 p32x_vdp_write8(a, d);
598 return;
599 }
600
601 // TODO: verify
602 if ((a & 0xfe00) == 0x5200) { // a15200
603 elprintf(EL_32X|EL_ANOMALY, "m68k 32x PAL w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
604 ((u8 *)Pico32xMem->pal)[(a & 0x1ff) ^ 1] = d;
605 Pico32x.dirty_pal = 1;
606 return;
607 }
608
609no_vdp:
610 elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
611}
612
613void PicoWrite16_32x(u32 a, u32 d)
614{
615 if ((a & 0xfc00) == 0x5000)
616 elprintf(EL_UIO, "m68k 32x w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
617
618 if ((a & 0xffc0) == 0x5100) { // a15100
619 p32x_reg_write16(a, d);
620 return;
621 }
622
623 if (!(Pico32x.regs[0] & 1))
624 goto no_vdp;
625
626 if ((a & 0xfff0) == 0x5180) { // a15180
627 p32x_vdp_write16(a, d);
628 return;
629 }
630
631 if ((a & 0xfe00) == 0x5200) { // a15200
632 Pico32xMem->pal[(a & 0x1ff) / 2] = d;
633 Pico32x.dirty_pal = 1;
634 return;
635 }
636
637no_vdp:
638 elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
639}
640
641// hint vector is writeable
642static void PicoWrite8_hint(u32 a, u32 d)
643{
644 if ((a & 0xfffc) == 0x0070) {
645 Pico32xMem->m68k_rom[a ^ 1] = d;
646 return;
647 }
648
649 elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc);
650}
651
652static void PicoWrite16_hint(u32 a, u32 d)
653{
654 if ((a & 0xfffc) == 0x0070) {
655 ((u16 *)Pico32xMem->m68k_rom)[a/2] = d;
656 return;
657 }
658
659 elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
660}
661
662void Pico32xSwapDRAM(int b)
663{
664 cpu68k_map_set(m68k_read8_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0);
665 cpu68k_map_set(m68k_read16_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0);
666 cpu68k_map_set(m68k_write8_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0);
667 cpu68k_map_set(m68k_write16_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0);
668}
669
670static void bank_switch(int b)
671{
672 unsigned int rs, bank;
673
674 bank = b << 20;
675 if (bank >= Pico.romsize) {
676 elprintf(EL_32X|EL_ANOMALY, "missing bank @ %06x", bank);
677 return;
678 }
679
680 // 32X ROM (unbanked, XXX: consider mirroring?)
681 rs = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK;
682 rs -= bank;
683 if (rs > 0x100000)
684 rs = 0x100000;
685 cpu68k_map_set(m68k_read8_map, 0x900000, 0x900000 + rs - 1, Pico.rom + bank, 0);
686 cpu68k_map_set(m68k_read16_map, 0x900000, 0x900000 + rs - 1, Pico.rom + bank, 0);
687
688 elprintf(EL_32X, "bank %06x-%06x -> %06x", 0x900000, 0x900000 + rs - 1, bank);
689}
690
691// -----------------------------------------------------------------
692// SH2
693// -----------------------------------------------------------------
694
695u32 p32x_sh2_read8(u32 a, int id)
696{
697 u32 d = 0;
698
699 if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m))
700 return Pico32xMem->sh2_rom_m[a ^ 1];
701 if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s))
702 return Pico32xMem->sh2_rom_s[a ^ 1];
703
704 if ((a & 0xdffc0000) == 0x06000000)
705 return Pico32xMem->sdram[(a & 0x3ffff) ^ 1];
706
707 if ((a & 0xdfc00000) == 0x02000000)
708 if ((a & 0x003fffff) < Pico.romsize)
709 return Pico.rom[(a & 0x3fffff) ^ 1];
710
711 if ((a & ~0xfff) == 0xc0000000)
712 return Pico32xMem->data_array[id][(a & 0xfff) ^ 1];
713
714 if ((a & 0xdffe0000) == 0x04000000) {
715 u8 *dram = (u8 *)Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1];
716 return dram[(a & 0x1ffff) ^ 1];
717 }
718
719 if ((a & 0xdfffff00) == 0x4000) {
720 d = p32x_sh2reg_read16(a, id);
721 goto out_16to8;
722 }
723
724 if ((a & 0xdfffff00) == 0x4100) {
725 d = p32x_vdp_read16(a);
726 if (p32x_poll_detect(&sh2_poll[id], a, ash2_cycles_done(), 1))
727 ash2_end_run(8);
728 goto out_16to8;
729 }
730
731 if ((a & 0xdfffff00) == 0x4200) {
732 d = Pico32xMem->pal[(a & 0x1ff) / 2];
733 goto out_16to8;
734 }
735
736 if ((a & 0xfffffe00) == 0xfffffe00)
737 return sh2_peripheral_read8(a, id);
738
739 elprintf(EL_UIO, "%csh2 unmapped r8 [%08x] %02x @%06x",
740 id ? 's' : 'm', a, d, sh2_pc(id));
741 return d;
742
743out_16to8:
744 if (a & 1)
745 d &= 0xff;
746 else
747 d >>= 8;
748
749 elprintf(EL_32X, "%csh2 r8 [%08x] %02x @%06x",
750 id ? 's' : 'm', a, d, sh2_pc(id));
751 return d;
752}
753
754u32 p32x_sh2_read16(u32 a, int id)
755{
756 u32 d = 0;
757
758 if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m))
759 return *(u16 *)(Pico32xMem->sh2_rom_m + a);
760 if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s))
761 return *(u16 *)(Pico32xMem->sh2_rom_s + a);
762
763 if ((a & 0xdffc0000) == 0x06000000)
764 return ((u16 *)Pico32xMem->sdram)[(a & 0x3ffff) / 2];
765
766 if ((a & 0xdfc00000) == 0x02000000)
767 if ((a & 0x003fffff) < Pico.romsize)
768 return ((u16 *)Pico.rom)[(a & 0x3fffff) / 2];
769
770 if ((a & ~0xfff) == 0xc0000000)
771 return ((u16 *)Pico32xMem->data_array[id])[(a & 0xfff) / 2];
772
773 if ((a & 0xdffe0000) == 0x04000000)
774 return Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1][(a & 0x1ffff) / 2];
775
776 if ((a & 0xdfffff00) == 0x4000) {
777 d = p32x_sh2reg_read16(a, id);
778 goto out;
779 }
780
781 if ((a & 0xdfffff00) == 0x4100) {
782 d = p32x_vdp_read16(a);
783 if (p32x_poll_detect(&sh2_poll[id], a, ash2_cycles_done(), 1))
784 ash2_end_run(8);
785 goto out;
786 }
787
788 if ((a & 0xdfffff00) == 0x4200) {
789 d = Pico32xMem->pal[(a & 0x1ff) / 2];
790 goto out;
791 }
792
793 elprintf(EL_UIO, "%csh2 unmapped r16 [%08x] %04x @%06x",
794 id ? 's' : 'm', a, d, sh2_pc(id));
795 return d;
796
797out:
798 elprintf(EL_32X, "%csh2 r16 [%08x] %04x @%06x",
799 id ? 's' : 'm', a, d, sh2_pc(id));
800 return d;
801}
802
803u32 p32x_sh2_read32(u32 a, int id)
804{
805 if ((a & 0xfffffe00) == 0xfffffe00)
806 return sh2_peripheral_read32(a, id);
807
808// elprintf(EL_UIO, "sh2 r32 [%08x] %08x @%06x", a, d, ash2_pc());
809 return (p32x_sh2_read16(a, id) << 16) | p32x_sh2_read16(a + 2, id);
810}
811
812void p32x_sh2_write8(u32 a, u32 d, int id)
813{
814 if ((a & 0xdffffc00) == 0x4000)
815 elprintf(EL_32X, "%csh2 w8 [%08x] %02x @%06x",
816 id ? 's' : 'm', a, d & 0xff, sh2_pc(id));
817
818 if ((a & 0xdffc0000) == 0x06000000) {
819 Pico32xMem->sdram[(a & 0x3ffff) ^ 1] = d;
820 return;
821 }
822
823 if ((a & 0xdffc0000) == 0x04000000) {
824 u8 *dram;
825 if (!(a & 0x20000) || d) {
826 dram = (u8 *)Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1];
827 dram[(a & 0x1ffff) ^ 1] = d;
828 return;
829 }
830 }
831
832 if ((a & ~0xfff) == 0xc0000000) {
833 Pico32xMem->data_array[id][(a & 0xfff) ^ 1] = d;
834 return;
835 }
836
837 if ((a & 0xdfffff00) == 0x4100) {
838 p32x_vdp_write8(a, d);
839 return;
840 }
841
842 if ((a & 0xdfffff00) == 0x4000) {
843 p32x_sh2reg_write8(a, d, id);
844 return;
845 }
846
847 if ((a & 0xfffffe00) == 0xfffffe00) {
848 sh2_peripheral_write8(a, d, id);
849 return;
850 }
851
852 elprintf(EL_UIO, "%csh2 unmapped w8 [%08x] %02x @%06x",
853 id ? 's' : 'm', a, d & 0xff, sh2_pc(id));
854}
855
856void p32x_sh2_write16(u32 a, u32 d, int id)
857{
858 if ((a & 0xdffffc00) == 0x4000)
859 elprintf(EL_32X, "%csh2 w16 [%08x] %04x @%06x",
860 id ? 's' : 'm', a, d & 0xffff, sh2_pc(id));
861
862 // ignore "Associative purge space"
863 if ((a & 0xf8000000) == 0x40000000)
864 return;
865
866 if ((a & 0xdffc0000) == 0x06000000) {
867 ((u16 *)Pico32xMem->sdram)[(a & 0x3ffff) / 2] = d;
868 return;
869 }
870
871 if ((a & ~0xfff) == 0xc0000000) {
872 ((u16 *)Pico32xMem->data_array[id])[(a & 0xfff) / 2] = d;
873 return;
874 }
875
876 if ((a & 0xdffc0000) == 0x04000000) {
877 u16 *pd = &Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1][(a & 0x1ffff) / 2];
878 if (!(a & 0x20000)) {
879 *pd = d;
880 return;
881 }
882 // overwrite
883 if (!(d & 0xff00)) d |= *pd & 0xff00;
884 if (!(d & 0x00ff)) d |= *pd & 0x00ff;
885 *pd = d;
886 return;
887 }
888
889 if ((a & 0xdfffff00) == 0x4100) {
890 sh2_poll[id].cnt = 0; // for poll before VDP accesses
891 p32x_vdp_write16(a, d);
892 return;
893 }
894
895 if ((a & 0xdffffe00) == 0x4200) {
896 Pico32xMem->pal[(a & 0x1ff) / 2] = d;
897 Pico32x.dirty_pal = 1;
898 return;
899 }
900
901 if ((a & 0xdfffff00) == 0x4000) {
902 p32x_sh2reg_write16(a, d, id);
903 return;
904 }
905
906 elprintf(EL_UIO, "%csh2 unmapped w16 [%08x] %04x @%06x",
907 id ? 's' : 'm', a, d & 0xffff, sh2_pc(id));
908}
909
910void p32x_sh2_write32(u32 a, u32 d, int id)
911{
912 if ((a & 0xfffffe00) == 0xfffffe00) {
913 sh2_peripheral_write32(a, d, id);
914 return;
915 }
916
917 p32x_sh2_write16(a, d >> 16, id);
918 p32x_sh2_write16(a + 2, d, id);
919}
920
921#define HWSWAP(x) (((x) << 16) | ((x) >> 16))
922void PicoMemSetup32x(void)
923{
924 unsigned short *ps;
925 unsigned int *pl;
926 unsigned int rs;
927 int i;
928
929 Pico32xMem = calloc(1, sizeof(*Pico32xMem));
930 if (Pico32xMem == NULL) {
931 elprintf(EL_STATUS, "OOM");
932 return;
933 }
934
935 dmac0 = (void *)&Pico32xMem->sh2_peri_regs[0][0x180 / 4];
936
937 // generate 68k ROM
938 ps = (unsigned short *)Pico32xMem->m68k_rom;
939 pl = (unsigned int *)Pico32xMem->m68k_rom;
940 for (i = 1; i < 0xc0/4; i++)
941 pl[i] = HWSWAP(0x880200 + (i - 1) * 6);
942
943 // fill with nops
944 for (i = 0xc0/2; i < 0x100/2; i++)
945 ps[i] = 0x4e71;
946
947#if 0
948 ps[0xc0/2] = 0x46fc;
949 ps[0xc2/2] = 0x2700; // move #0x2700,sr
950 ps[0xfe/2] = 0x60fe; // jump to self
951#else
952 ps[0xfe/2] = 0x4e75; // rts
953#endif
954
955 // fill remaining mem with ROM
956 memcpy(Pico32xMem->m68k_rom + 0x100, Pico.rom + 0x100, sizeof(Pico32xMem->m68k_rom) - 0x100);
957
958 // 32X ROM
959 // TODO: move
960 {
961 FILE *f = fopen("32X_M_BIOS.BIN", "rb");
962 int i;
963 if (f == NULL) {
964 printf("missing 32X_M_BIOS.BIN\n");
965 exit(1);
966 }
967 fread(Pico32xMem->sh2_rom_m, 1, sizeof(Pico32xMem->sh2_rom_m), f);
968 fclose(f);
969 f = fopen("32X_S_BIOS.BIN", "rb");
970 if (f == NULL) {
971 printf("missing 32X_S_BIOS.BIN\n");
972 exit(1);
973 }
974 fread(Pico32xMem->sh2_rom_s, 1, sizeof(Pico32xMem->sh2_rom_s), f);
975 fclose(f);
976 // byteswap
977 for (i = 0; i < sizeof(Pico32xMem->sh2_rom_m); i += 2) {
978 int t = Pico32xMem->sh2_rom_m[i];
979 Pico32xMem->sh2_rom_m[i] = Pico32xMem->sh2_rom_m[i + 1];
980 Pico32xMem->sh2_rom_m[i + 1] = t;
981 }
982 for (i = 0; i < sizeof(Pico32xMem->sh2_rom_s); i += 2) {
983 int t = Pico32xMem->sh2_rom_s[i];
984 Pico32xMem->sh2_rom_s[i] = Pico32xMem->sh2_rom_s[i + 1];
985 Pico32xMem->sh2_rom_s[i + 1] = t;
986 }
987 }
988
989 // cartridge area becomes unmapped
990 // XXX: we take the easy way and don't unmap ROM,
991 // so that we can avoid handling the RV bit.
992 // m68k_map_unmap(0x000000, 0x3fffff);
993
994 // MD ROM area
995 rs = sizeof(Pico32xMem->m68k_rom);
996 cpu68k_map_set(m68k_read8_map, 0x000000, rs - 1, Pico32xMem->m68k_rom, 0);
997 cpu68k_map_set(m68k_read16_map, 0x000000, rs - 1, Pico32xMem->m68k_rom, 0);
998 cpu68k_map_set(m68k_write8_map, 0x000000, rs - 1, PicoWrite8_hint, 1); // TODO verify
999 cpu68k_map_set(m68k_write16_map, 0x000000, rs - 1, PicoWrite16_hint, 1);
1000
1001 // DRAM area
1002 Pico32xSwapDRAM(1);
1003
1004 // 32X ROM (unbanked, XXX: consider mirroring?)
1005 rs = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK;
1006 if (rs > 0x80000)
1007 rs = 0x80000;
1008 cpu68k_map_set(m68k_read8_map, 0x880000, 0x880000 + rs - 1, Pico.rom, 0);
1009 cpu68k_map_set(m68k_read16_map, 0x880000, 0x880000 + rs - 1, Pico.rom, 0);
1010
1011 // 32X ROM (banked)
1012 bank_switch(0);
1013
1014 // setup poll detector
1015 m68k_poll.flag = P32XF_68KPOLL;
1016 m68k_poll.cyc_max = 64;
1017 sh2_poll[0].flag = P32XF_MSH2POLL;
1018 sh2_poll[0].cyc_max = 16;
1019 sh2_poll[1].flag = P32XF_SSH2POLL;
1020 sh2_poll[1].cyc_max = 16;
1021}
1022