32x: implement VDP fill, improve PWM, division unit and scheduling
[picodrive.git] / pico / 32x / memory.c
CommitLineData
be2c4208 1#include "../pico_int.h"
2#include "../memory.h"
3
4static const char str_mars[] = "MARS";
5
974fdb5b 6struct Pico32xMem *Pico32xMem;
7
5e49c3a8 8static void bank_switch(int b);
9
266c6afa 10// poll detection
4ea707e1 11#define POLL_THRESHOLD 6
12
266c6afa 13struct poll_det {
be20816c 14 u32 addr, cycles, cyc_max;
15 int cnt, flag;
266c6afa 16};
b78efee2 17static struct poll_det m68k_poll, sh2_poll[2];
266c6afa 18
be20816c 19static int p32x_poll_detect(struct poll_det *pd, u32 a, u32 cycles, int is_vdp)
266c6afa 20{
b78efee2 21 int ret = 0, flag = pd->flag;
22
23 if (is_vdp)
24 flag <<= 3;
266c6afa 25
be20816c 26 if (a - 2 <= pd->addr && pd->addr <= a + 2 && cycles - pd->cycles < pd->cyc_max) {
266c6afa 27 pd->cnt++;
28 if (pd->cnt > POLL_THRESHOLD) {
29 if (!(Pico32x.emu_flags & flag)) {
be20816c 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);
266c6afa 33 ret = 1;
34 }
35 Pico32x.emu_flags |= flag;
36 }
37 }
38 else
39 pd->cnt = 0;
40 pd->addr = a;
be20816c 41 pd->cycles = cycles;
266c6afa 42
43 return ret;
44}
45
b78efee2 46static int p32x_poll_undetect(struct poll_det *pd, int is_vdp)
266c6afa 47{
b78efee2 48 int ret = 0, flag = pd->flag;
49 if (is_vdp)
be20816c 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);
266c6afa 55 ret = 1;
be20816c 56 }
266c6afa 57 Pico32x.emu_flags &= ~flag;
be20816c 58 pd->addr = pd->cnt = 0;
266c6afa 59 return ret;
60}
61
87accdf7 62void p32x_poll_event(int cpu_mask, int is_vdp)
4ea707e1 63{
87accdf7 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);
4ea707e1 68}
69
974fdb5b 70// SH2 faking
b78efee2 71//#define FAKE_SH2
acd35d4c 72int p32x_csum_faked;
73#ifdef FAKE_SH2
974fdb5b 74static const u16 comm_fakevals[] = {
75 0x4d5f, 0x4f4b, // M_OK
76 0x535f, 0x4f4b, // S_OK
5e49c3a8 77 0x4D41, 0x5346, // MASF - Brutal Unleashed
78 0x5331, 0x4d31, // Darxide
79 0x5332, 0x4d32,
80 0x5333, 0x4d33,
81 0x0000, 0x0000, // eq for doom
974fdb5b 82 0x0002, // Mortal Kombat
acd35d4c 83// 0, // pad
be2c4208 84};
acd35d4c 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
be2c4208 98
4ea707e1 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++) {
b78efee2 118 extern void p32x_sh2_write16(u32 a, u32 d, int id);
be20816c 119 elprintf(EL_32X, "dmaw [%08x] %04x, left %d", dmac0->dar0, Pico32x.dmac_fifo[i], *dreqlen);
b78efee2 120 p32x_sh2_write16(dmac0->dar0, Pico32x.dmac_fifo[i], 0);
4ea707e1 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
be20816c 130 if (dmac0->tcr0 == 0) {
4ea707e1 131 dmac0->chcr0 |= 2; // DMA has ended normally
be20816c 132 p32x_poll_undetect(&sh2_poll[0], 0);
133 }
4ea707e1 134}
135
136// ------------------------------------------------------------------
b78efee2 137// 68k regs
4ea707e1 138
be2c4208 139static u32 p32x_reg_read16(u32 a)
140{
141 a &= 0x3e;
142
87accdf7 143 if (a == 2) // INTM, INTS
144 return ((Pico32x.sh2irqi[0] & P32XI_CMD) >> 4) | ((Pico32x.sh2irqi[1] & P32XI_CMD) >> 3);
3cf9570b 145#if 0
974fdb5b 146 if ((a & 0x30) == 0x20)
acd35d4c 147 return sh2_comm_faker(a);
266c6afa 148#else
be20816c 149 if ((a & 0x30) == 0x20 && p32x_poll_detect(&m68k_poll, a, SekCyclesDoneT(), 0)) {
266c6afa 150 SekEndRun(16);
151 }
acd35d4c 152#endif
87accdf7 153
db1d3564 154 if ((a & 0x30) == 0x30)
155 return p32x_pwm_read16(a);
974fdb5b 156
be2c4208 157 return Pico32x.regs[a / 2];
158}
159
be2c4208 160static void p32x_reg_write8(u32 a, u32 d)
161{
acd35d4c 162 u16 *r = Pico32x.regs;
be2c4208 163 a &= 0x3f;
164
97d3f47f 165 // for things like bset on comm port
166 m68k_poll.cnt = 0;
167
acd35d4c 168 if (a == 1 && !(r[0] & 1)) {
169 r[0] |= 1;
be2c4208 170 Pico32xStartup();
171 return;
172 }
5e49c3a8 173
acd35d4c 174 if (!(r[0] & 1))
5e49c3a8 175 return;
176
acd35d4c 177 switch (a) {
4ea707e1 178 case 0: // adapter ctl
acd35d4c 179 r[0] = (r[0] & 0x83) | ((d << 8) & P32XS_FM);
180 break;
4ea707e1 181 case 3: // irq ctl
182 if ((d & 1) && !(Pico32x.sh2irqi[0] & P32XI_CMD)) {
183 Pico32x.sh2irqi[0] |= P32XI_CMD;
184 p32x_update_irls();
87accdf7 185 SekEndRun(16);
4ea707e1 186 }
b78efee2 187 if ((d & 2) && !(Pico32x.sh2irqi[1] & P32XI_CMD)) {
188 Pico32x.sh2irqi[1] |= P32XI_CMD;
189 p32x_update_irls();
87accdf7 190 SekEndRun(16);
b78efee2 191 }
4ea707e1 192 break;
193 case 5: // bank
acd35d4c 194 d &= 7;
4ea707e1 195 if (r[4 / 2] != d) {
196 r[4 / 2] = d;
acd35d4c 197 bank_switch(d);
198 }
199 break;
4ea707e1 200 case 7: // DREQ ctl
97d3f47f 201 r[6 / 2] = (r[6 / 2] & P32XS_FULL) | (d & (P32XS_68S|P32XS_DMA|P32XS_RV));
4ea707e1 202 break;
87accdf7 203 case 0x1b: // TV
204 r[0x1a / 2] = d;
205 break;
5e49c3a8 206 }
207}
208
209static void p32x_reg_write16(u32 a, u32 d)
210{
acd35d4c 211 u16 *r = Pico32x.regs;
212 a &= 0x3e;
213
97d3f47f 214 // for things like bset on comm port
215 m68k_poll.cnt = 0;
216
acd35d4c 217 switch (a) {
4ea707e1 218 case 0x00: // adapter ctl
acd35d4c 219 r[0] = (r[0] & 0x83) | (d & P32XS_FM);
220 return;
4ea707e1 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;
acd35d4c 237 }
238
4ea707e1 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) {
acd35d4c 246 r[a / 2] = d;
b78efee2 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
3cf9570b 249 SekEndRun(16);
acd35d4c 250 return;
251 }
db1d3564 252 // PWM
253 else if ((a & 0x30) == 0x30) {
254 p32x_pwm_write16(a, d);
255 return;
256 }
acd35d4c 257
5e49c3a8 258 p32x_reg_write8(a + 1, d);
be2c4208 259}
260
4ea707e1 261// ------------------------------------------------------------------
be2c4208 262// VDP regs
263static u32 p32x_vdp_read16(u32 a)
264{
265 a &= 0x0e;
266
267 return Pico32x.vdp_regs[a / 2];
268}
269
be2c4208 270static void p32x_vdp_write8(u32 a, u32 d)
271{
974fdb5b 272 u16 *r = Pico32x.vdp_regs;
be2c4208 273 a &= 0x0f;
274
4ea707e1 275 // for FEN checks between writes
b78efee2 276 sh2_poll[0].cnt = 0;
4ea707e1 277
974fdb5b 278 // TODO: verify what's writeable
be2c4208 279 switch (a) {
974fdb5b 280 case 0x01:
5e49c3a8 281 // priority inversion is handled in palette
282 if ((r[0] ^ d) & P32XV_PRI)
283 Pico32x.dirty_pal = 1;
974fdb5b 284 r[0] = (r[0] & P32XV_nPAL) | (d & 0xff);
be20816c 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;
974fdb5b 290 break;
be2c4208 291 case 0x0b:
974fdb5b 292 d &= 1;
293 Pico32x.pending_fb = d;
294 // if we are blanking and FS bit is changing
4ea707e1 295 if (((r[0x0a/2] & P32XV_VBLK) || (r[0] & P32XV_Mx) == 0) && ((r[0x0a/2] ^ d) & P32XV_FS)) {
974fdb5b 296 r[0x0a/2] ^= 1;
297 Pico32xSwapDRAM(d ^ 1);
266c6afa 298 elprintf(EL_32X, "VDP FS: %d", r[0x0a/2] & P32XV_FS);
be2c4208 299 }
300 break;
301 }
302}
303
974fdb5b 304static void p32x_vdp_write16(u32 a, u32 d)
305{
be20816c 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
974fdb5b 324 p32x_vdp_write8(a | 1, d);
325}
326
4ea707e1 327// ------------------------------------------------------------------
acd35d4c 328// SH2 regs
b78efee2 329
330static u32 p32x_sh2reg_read16(u32 a, int cpuid)
acd35d4c 331{
4ea707e1 332 u16 *r = Pico32x.regs;
333 a &= 0xfe; // ?
266c6afa 334
4ea707e1 335 switch (a) {
336 case 0x00: // adapter/irq ctl
87accdf7 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];
4ea707e1 340 case 0x10: // DREQ len
341 return r[a / 2];
acd35d4c 342 }
4ea707e1 343
db1d3564 344 // DREQ src, dst
345 if ((a & 0x38) == 0x08)
4ea707e1 346 return r[a / 2];
db1d3564 347 // comm port
348 if ((a & 0x30) == 0x20) {
be20816c 349 if (p32x_poll_detect(&sh2_poll[cpuid], a, ash2_cycles_done(), 0))
db1d3564 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 }
acd35d4c 357
358 return 0;
359}
360
b78efee2 361static void p32x_sh2reg_write8(u32 a, u32 d, int cpuid)
acd35d4c 362{
4ea707e1 363 a &= 0xff;
87accdf7 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;
4ea707e1 378 }
acd35d4c 379}
380
b78efee2 381static void p32x_sh2reg_write16(u32 a, u32 d, int cpuid)
acd35d4c 382{
4ea707e1 383 a &= 0xfe;
acd35d4c 384
db1d3564 385 // comm
4ea707e1 386 if ((a & 0x30) == 0x20 && Pico32x.regs[a/2] != d) {
b78efee2 387 Pico32x.regs[a / 2] = d;
388 p32x_poll_undetect(&m68k_poll, 0);
389 p32x_poll_undetect(&sh2_poll[cpuid ^ 1], 0);
acd35d4c 390 return;
391 }
db1d3564 392 // PWM
393 else if ((a & 0x30) == 0x30) {
394 p32x_pwm_write16(a, d);
395 return;
396 }
acd35d4c 397
4ea707e1 398 switch (a) {
87accdf7 399 case 0: // FM
400 Pico32x.regs[0] &= ~P32XS_FM;
401 Pico32x.regs[0] |= d & P32XS_FM;
402 break;
4ea707e1 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;
b78efee2 406 case 0x1a: Pico32x.sh2irqi[cpuid] &= ~P32XI_CMD; goto irls;
be20816c 407 case 0x1c:
408 Pico32x.sh2irqs &= ~P32XI_PWM;
409 p32x_pwm_irq_check(0);
410 goto irls;
4ea707e1 411 }
412
b78efee2 413 p32x_sh2reg_write8(a | 1, d, cpuid);
4ea707e1 414 return;
415
416irls:
417 p32x_update_irls();
418}
419
87accdf7 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)
4ea707e1 437{
438 u32 d;
439 a &= 0x1fc;
97d3f47f 440 d = Pico32xMem->sh2_peri_regs[id][a / 4];
4ea707e1 441
97d3f47f 442 elprintf(EL_32X, "%csh2 peri r32 [%08x] %08x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id));
4ea707e1 443 return d;
acd35d4c 444}
445
87accdf7 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)
4ea707e1 456{
be20816c 457 u32 *r = Pico32xMem->sh2_peri_regs[id];
b78efee2 458 elprintf(EL_32X, "%csh2 peri w32 [%08x] %08x @%06x", id ? 's' : 'm', a, d, sh2_pc(id));
4ea707e1 459
460 a &= 0x1fc;
461 r[a / 4] = d;
462
97d3f47f 463 switch (a) {
be20816c 464 // division unit (TODO: verify):
97d3f47f 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]) {
be20816c 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;
97d3f47f 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]) {
be20816c 477 signed long long divident = (signed long long)r[0x110 / 4] << 32 | d;
478 signed int divisor = r[0x100 / 4];
97d3f47f 479 // XXX: undocumented mirroring to 0x118,0x11c?
be20816c 480 r[0x118 / 4] = r[0x110 / 4] = divident % divisor;
481 r[0x11c / 4] = r[0x114 / 4] = divident / divisor;
97d3f47f 482 }
483 break;
484 }
485
4ea707e1 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",
b78efee2 488 dmac0->sar0, dmac0->dar0, dmac0->tcr0, dmac0->chcr0, sh2_pc(id));
4ea707e1 489 dmac0->tcr0 &= 0xffffff;
be20816c 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
4ea707e1 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// ------------------------------------------------------------------
be2c4208 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
974fdb5b 515 if (!(Pico32x.regs[0] & 1))
516 goto no_vdp;
517
518 if ((a & 0xfff0) == 0x5180) { // a15180
be2c4208 519 d = p32x_vdp_read16(a);
520 goto out_16to8;
521 }
522
974fdb5b 523 if ((a & 0xfe00) == 0x5200) { // a15200
524 d = Pico32xMem->pal[(a & 0x1ff) / 2];
525 goto out_16to8;
526 }
527
528no_vdp:
be2c4208 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
974fdb5b 556 if (!(Pico32x.regs[0] & 1))
557 goto no_vdp;
558
559 if ((a & 0xfff0) == 0x5180) { // a15180
be2c4208 560 d = p32x_vdp_read16(a);
561 goto out;
562 }
563
974fdb5b 564 if ((a & 0xfe00) == 0x5200) { // a15200
565 d = Pico32xMem->pal[(a & 0x1ff) / 2];
566 goto out;
567 }
568
569no_vdp:
be2c4208 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
974fdb5b 593 if (!(Pico32x.regs[0] & 1))
594 goto no_vdp;
595
596 if ((a & 0xfff0) == 0x5180) { // a15180
be2c4208 597 p32x_vdp_write8(a, d);
598 return;
599 }
600
974fdb5b 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:
be2c4208 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
974fdb5b 623 if (!(Pico32x.regs[0] & 1))
624 goto no_vdp;
625
626 if ((a & 0xfff0) == 0x5180) { // a15180
be2c4208 627 p32x_vdp_write16(a, d);
628 return;
629 }
630
974fdb5b 631 if ((a & 0xfe00) == 0x5200) { // a15200
632 Pico32xMem->pal[(a & 0x1ff) / 2] = d;
633 Pico32x.dirty_pal = 1;
634 return;
635 }
636
637no_vdp:
be2c4208 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
974fdb5b 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
5e49c3a8 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
acd35d4c 691// -----------------------------------------------------------------
692// SH2
693// -----------------------------------------------------------------
694
b78efee2 695u32 p32x_sh2_read8(u32 a, int id)
acd35d4c 696{
697 u32 d = 0;
4ea707e1 698
b78efee2 699 if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m))
acd35d4c 700 return Pico32xMem->sh2_rom_m[a ^ 1];
b78efee2 701 if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s))
702 return Pico32xMem->sh2_rom_s[a ^ 1];
acd35d4c 703
87accdf7 704 if ((a & 0xdffc0000) == 0x06000000)
acd35d4c 705 return Pico32xMem->sdram[(a & 0x3ffff) ^ 1];
706
87accdf7 707 if ((a & 0xdfc00000) == 0x02000000)
acd35d4c 708 if ((a & 0x003fffff) < Pico.romsize)
709 return Pico.rom[(a & 0x3fffff) ^ 1];
710
b78efee2 711 if ((a & ~0xfff) == 0xc0000000)
712 return Pico32xMem->data_array[id][(a & 0xfff) ^ 1];
713
87accdf7 714 if ((a & 0xdffe0000) == 0x04000000) {
97d3f47f 715 u8 *dram = (u8 *)Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1];
716 return dram[(a & 0x1ffff) ^ 1];
717 }
718
87accdf7 719 if ((a & 0xdfffff00) == 0x4000) {
b78efee2 720 d = p32x_sh2reg_read16(a, id);
db1d3564 721 goto out_16to8;
acd35d4c 722 }
723
87accdf7 724 if ((a & 0xdfffff00) == 0x4100) {
acd35d4c 725 d = p32x_vdp_read16(a);
be20816c 726 if (p32x_poll_detect(&sh2_poll[id], a, ash2_cycles_done(), 1))
db1d3564 727 ash2_end_run(8);
728 goto out_16to8;
acd35d4c 729 }
730
87accdf7 731 if ((a & 0xdfffff00) == 0x4200) {
acd35d4c 732 d = Pico32xMem->pal[(a & 0x1ff) / 2];
733 goto out_16to8;
734 }
735
87accdf7 736 if ((a & 0xfffffe00) == 0xfffffe00)
737 return sh2_peripheral_read8(a, id);
738
b78efee2 739 elprintf(EL_UIO, "%csh2 unmapped r8 [%08x] %02x @%06x",
740 id ? 's' : 'm', a, d, sh2_pc(id));
acd35d4c 741 return d;
742
743out_16to8:
744 if (a & 1)
745 d &= 0xff;
746 else
747 d >>= 8;
748
b78efee2 749 elprintf(EL_32X, "%csh2 r8 [%08x] %02x @%06x",
750 id ? 's' : 'm', a, d, sh2_pc(id));
acd35d4c 751 return d;
752}
753
b78efee2 754u32 p32x_sh2_read16(u32 a, int id)
acd35d4c 755{
756 u32 d = 0;
3cf9570b 757
b78efee2 758 if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m))
acd35d4c 759 return *(u16 *)(Pico32xMem->sh2_rom_m + a);
b78efee2 760 if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s))
761 return *(u16 *)(Pico32xMem->sh2_rom_s + a);
acd35d4c 762
87accdf7 763 if ((a & 0xdffc0000) == 0x06000000)
acd35d4c 764 return ((u16 *)Pico32xMem->sdram)[(a & 0x3ffff) / 2];
765
87accdf7 766 if ((a & 0xdfc00000) == 0x02000000)
acd35d4c 767 if ((a & 0x003fffff) < Pico.romsize)
768 return ((u16 *)Pico.rom)[(a & 0x3fffff) / 2];
769
b78efee2 770 if ((a & ~0xfff) == 0xc0000000)
771 return ((u16 *)Pico32xMem->data_array[id])[(a & 0xfff) / 2];
772
87accdf7 773 if ((a & 0xdffe0000) == 0x04000000)
97d3f47f 774 return Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1][(a & 0x1ffff) / 2];
775
87accdf7 776 if ((a & 0xdfffff00) == 0x4000) {
b78efee2 777 d = p32x_sh2reg_read16(a, id);
db1d3564 778 goto out;
acd35d4c 779 }
780
87accdf7 781 if ((a & 0xdfffff00) == 0x4100) {
acd35d4c 782 d = p32x_vdp_read16(a);
be20816c 783 if (p32x_poll_detect(&sh2_poll[id], a, ash2_cycles_done(), 1))
db1d3564 784 ash2_end_run(8);
785 goto out;
acd35d4c 786 }
787
87accdf7 788 if ((a & 0xdfffff00) == 0x4200) {
acd35d4c 789 d = Pico32xMem->pal[(a & 0x1ff) / 2];
790 goto out;
791 }
792
b78efee2 793 elprintf(EL_UIO, "%csh2 unmapped r16 [%08x] %04x @%06x",
794 id ? 's' : 'm', a, d, sh2_pc(id));
acd35d4c 795 return d;
796
797out:
b78efee2 798 elprintf(EL_32X, "%csh2 r16 [%08x] %04x @%06x",
799 id ? 's' : 'm', a, d, sh2_pc(id));
acd35d4c 800 return d;
801}
802
b78efee2 803u32 p32x_sh2_read32(u32 a, int id)
acd35d4c 804{
4ea707e1 805 if ((a & 0xfffffe00) == 0xfffffe00)
87accdf7 806 return sh2_peripheral_read32(a, id);
4ea707e1 807
acd35d4c 808// elprintf(EL_UIO, "sh2 r32 [%08x] %08x @%06x", a, d, ash2_pc());
b78efee2 809 return (p32x_sh2_read16(a, id) << 16) | p32x_sh2_read16(a + 2, id);
acd35d4c 810}
811
b78efee2 812void p32x_sh2_write8(u32 a, u32 d, int id)
acd35d4c 813{
87accdf7 814 if ((a & 0xdffffc00) == 0x4000)
b78efee2 815 elprintf(EL_32X, "%csh2 w8 [%08x] %02x @%06x",
816 id ? 's' : 'm', a, d & 0xff, sh2_pc(id));
acd35d4c 817
87accdf7 818 if ((a & 0xdffc0000) == 0x06000000) {
acd35d4c 819 Pico32xMem->sdram[(a & 0x3ffff) ^ 1] = d;
820 return;
821 }
822
87accdf7 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 }
266c6afa 830 }
831
b78efee2 832 if ((a & ~0xfff) == 0xc0000000) {
833 Pico32xMem->data_array[id][(a & 0xfff) ^ 1] = d;
834 return;
835 }
836
87accdf7 837 if ((a & 0xdfffff00) == 0x4100) {
acd35d4c 838 p32x_vdp_write8(a, d);
839 return;
840 }
841
87accdf7 842 if ((a & 0xdfffff00) == 0x4000) {
b78efee2 843 p32x_sh2reg_write8(a, d, id);
acd35d4c 844 return;
845 }
846
87accdf7 847 if ((a & 0xfffffe00) == 0xfffffe00) {
848 sh2_peripheral_write8(a, d, id);
849 return;
850 }
851
b78efee2 852 elprintf(EL_UIO, "%csh2 unmapped w8 [%08x] %02x @%06x",
853 id ? 's' : 'm', a, d & 0xff, sh2_pc(id));
acd35d4c 854}
855
b78efee2 856void p32x_sh2_write16(u32 a, u32 d, int id)
acd35d4c 857{
87accdf7 858 if ((a & 0xdffffc00) == 0x4000)
b78efee2 859 elprintf(EL_32X, "%csh2 w16 [%08x] %04x @%06x",
860 id ? 's' : 'm', a, d & 0xffff, sh2_pc(id));
acd35d4c 861
87accdf7 862 // ignore "Associative purge space"
863 if ((a & 0xf8000000) == 0x40000000)
864 return;
865
866 if ((a & 0xdffc0000) == 0x06000000) {
acd35d4c 867 ((u16 *)Pico32xMem->sdram)[(a & 0x3ffff) / 2] = d;
868 return;
869 }
870
b78efee2 871 if ((a & ~0xfff) == 0xc0000000) {
872 ((u16 *)Pico32xMem->data_array[id])[(a & 0xfff) / 2] = d;
873 return;
874 }
875
87accdf7 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;
266c6afa 886 return;
887 }
888
87accdf7 889 if ((a & 0xdfffff00) == 0x4100) {
be20816c 890 sh2_poll[id].cnt = 0; // for poll before VDP accesses
acd35d4c 891 p32x_vdp_write16(a, d);
892 return;
893 }
894
87accdf7 895 if ((a & 0xdffffe00) == 0x4200) {
acd35d4c 896 Pico32xMem->pal[(a & 0x1ff) / 2] = d;
897 Pico32x.dirty_pal = 1;
898 return;
899 }
900
87accdf7 901 if ((a & 0xdfffff00) == 0x4000) {
b78efee2 902 p32x_sh2reg_write16(a, d, id);
acd35d4c 903 return;
904 }
905
b78efee2 906 elprintf(EL_UIO, "%csh2 unmapped w16 [%08x] %04x @%06x",
907 id ? 's' : 'm', a, d & 0xffff, sh2_pc(id));
acd35d4c 908}
909
b78efee2 910void p32x_sh2_write32(u32 a, u32 d, int id)
acd35d4c 911{
4ea707e1 912 if ((a & 0xfffffe00) == 0xfffffe00) {
87accdf7 913 sh2_peripheral_write32(a, d, id);
4ea707e1 914 return;
915 }
916
b78efee2 917 p32x_sh2_write16(a, d >> 16, id);
918 p32x_sh2_write16(a + 2, d, id);
acd35d4c 919}
920
be2c4208 921#define HWSWAP(x) (((x) << 16) | ((x) >> 16))
922void PicoMemSetup32x(void)
923{
924 unsigned short *ps;
925 unsigned int *pl;
5e49c3a8 926 unsigned int rs;
be2c4208 927 int i;
928
929 Pico32xMem = calloc(1, sizeof(*Pico32xMem));
930 if (Pico32xMem == NULL) {
931 elprintf(EL_STATUS, "OOM");
932 return;
933 }
934
4ea707e1 935 dmac0 = (void *)&Pico32xMem->sh2_peri_regs[0][0x180 / 4];
936
be2c4208 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++)
974fdb5b 941 pl[i] = HWSWAP(0x880200 + (i - 1) * 6);
be2c4208 942
943 // fill with nops
944 for (i = 0xc0/2; i < 0x100/2; i++)
945 ps[i] = 0x4e71;
946
5e49c3a8 947#if 0
be2c4208 948 ps[0xc0/2] = 0x46fc;
949 ps[0xc2/2] = 0x2700; // move #0x2700,sr
950 ps[0xfe/2] = 0x60fe; // jump to self
5e49c3a8 951#else
952 ps[0xfe/2] = 0x4e75; // rts
953#endif
be2c4208 954
955 // fill remaining mem with ROM
974fdb5b 956 memcpy(Pico32xMem->m68k_rom + 0x100, Pico.rom + 0x100, sizeof(Pico32xMem->m68k_rom) - 0x100);
be2c4208 957
acd35d4c 958 // 32X ROM
959 // TODO: move
960 {
961 FILE *f = fopen("32X_M_BIOS.BIN", "rb");
962 int i;
963 if (f == NULL) {
b78efee2 964 printf("missing 32X_M_BIOS.BIN\n");
acd35d4c 965 exit(1);
966 }
967 fread(Pico32xMem->sh2_rom_m, 1, sizeof(Pico32xMem->sh2_rom_m), f);
968 fclose(f);
b78efee2 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
acd35d4c 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 }
b78efee2 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 }
acd35d4c 987 }
988
be2c4208 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
974fdb5b 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);
be2c4208 1003
1004 // 32X ROM (unbanked, XXX: consider mirroring?)
5e49c3a8 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);
be2c4208 1010
1011 // 32X ROM (banked)
5e49c3a8 1012 bank_switch(0);
b78efee2 1013
1014 // setup poll detector
1015 m68k_poll.flag = P32XF_68KPOLL;
be20816c 1016 m68k_poll.cyc_max = 64;
b78efee2 1017 sh2_poll[0].flag = P32XF_MSH2POLL;
be20816c 1018 sh2_poll[0].cyc_max = 16;
b78efee2 1019 sh2_poll[1].flag = P32XF_SSH2POLL;
be20816c 1020 sh2_poll[1].cyc_max = 16;
be2c4208 1021}
1022