e1a892aee51bd32377a6589cba76568cfd9e96c1
[picodrive.git] / pico / memory.c
1 /*\r
2  * memory handling\r
3  * (c) Copyright Dave, 2004\r
4  * (C) notaz, 2006-2010\r
5  * (C) irixxxx, 2019-2024\r
6  *\r
7  * This work is licensed under the terms of MAME license.\r
8  * See COPYING file in the top-level directory.\r
9  */\r
10 \r
11 #include <assert.h>\r
12 #include "pico_int.h"\r
13 #include "memory.h"\r
14 #include "state.h"\r
15 \r
16 #include "sound/ym2612.h"\r
17 #include "sound/sn76496.h"\r
18 \r
19 extern unsigned int lastSSRamWrite; // used by serial eeprom code\r
20 \r
21 uptr m68k_read8_map  [0x1000000 >> M68K_MEM_SHIFT];\r
22 uptr m68k_read16_map [0x1000000 >> M68K_MEM_SHIFT];\r
23 uptr m68k_write8_map [0x1000000 >> M68K_MEM_SHIFT];\r
24 uptr m68k_write16_map[0x1000000 >> M68K_MEM_SHIFT];\r
25 \r
26 static void xmap_set(uptr *map, int shift, u32 start_addr, u32 end_addr,\r
27     const void *func_or_mh, int is_func)\r
28 {\r
29 #ifdef __clang__\r
30   // workaround bug (segfault) in \r
31   // Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn)\r
32   volatile \r
33 #endif\r
34   uptr addr = (uptr)func_or_mh;\r
35   int mask = (1 << shift) - 1;\r
36   int i;\r
37 \r
38   if ((start_addr & mask) != 0 || (end_addr & mask) != mask) {\r
39     elprintf(EL_STATUS|EL_ANOMALY, "xmap_set: tried to map bad range: %06x-%06x",\r
40       start_addr, end_addr);\r
41     return;\r
42   }\r
43 \r
44   if (addr & 1) {\r
45     elprintf(EL_STATUS|EL_ANOMALY, "xmap_set: ptr is not aligned: %08lx", addr);\r
46     return;\r
47   }\r
48 \r
49   if (!is_func)\r
50     addr -= start_addr;\r
51 \r
52   for (i = start_addr >> shift; i <= end_addr >> shift; i++) {\r
53     map[i] = addr >> 1;\r
54     if (is_func)\r
55       map[i] |= MAP_FLAG;\r
56   }\r
57 }\r
58 \r
59 void z80_map_set(uptr *map, u16 start_addr, u16 end_addr,\r
60     const void *func_or_mh, int is_func)\r
61 {\r
62   xmap_set(map, Z80_MEM_SHIFT, start_addr, end_addr, func_or_mh, is_func);\r
63 #ifdef _USE_CZ80\r
64   if (!is_func)\r
65     Cz80_Set_Fetch(&CZ80, start_addr, end_addr, (FPTR)func_or_mh);\r
66 #endif\r
67 }\r
68 \r
69 void cpu68k_map_set(uptr *map, u32 start_addr, u32 end_addr,\r
70     const void *func_or_mh, int is_func)\r
71 {\r
72   xmap_set(map, M68K_MEM_SHIFT, start_addr, end_addr, func_or_mh, is_func & 1);\r
73 #ifdef EMU_F68K\r
74   // setup FAME fetchmap\r
75   if (!(is_func & 1))\r
76   {\r
77     M68K_CONTEXT *ctx = is_func & 2 ? &PicoCpuFS68k : &PicoCpuFM68k;\r
78     int shiftout = 24 - FAMEC_FETCHBITS;\r
79     int i = start_addr >> shiftout;\r
80     uptr base = (uptr)func_or_mh - (i << shiftout);\r
81     for (; i <= (end_addr >> shiftout); i++)\r
82       ctx->Fetch[i] = base;\r
83   }\r
84 #endif\r
85 }\r
86 \r
87 // more specialized/optimized function (does same as above)\r
88 void cpu68k_map_read_mem(u32 start_addr, u32 end_addr, void *ptr, int is_sub)\r
89 {\r
90   uptr *r8map, *r16map;\r
91   uptr addr = (uptr)ptr;\r
92   int shift = M68K_MEM_SHIFT;\r
93   int i;\r
94 \r
95   if (!is_sub) {\r
96     r8map = m68k_read8_map;\r
97     r16map = m68k_read16_map;\r
98   } else {\r
99     r8map = s68k_read8_map;\r
100     r16map = s68k_read16_map;\r
101   }\r
102 \r
103   addr -= start_addr;\r
104   addr >>= 1;\r
105   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
106     r8map[i] = r16map[i] = addr;\r
107 #ifdef EMU_F68K\r
108   // setup FAME fetchmap\r
109   {\r
110     M68K_CONTEXT *ctx = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;\r
111     int shiftout = 24 - FAMEC_FETCHBITS;\r
112     i = start_addr >> shiftout;\r
113     addr = (uptr)ptr - (i << shiftout);\r
114     for (; i <= (end_addr >> shiftout); i++)\r
115       ctx->Fetch[i] = addr;\r
116   }\r
117 #endif\r
118 }\r
119 \r
120 void cpu68k_map_all_ram(u32 start_addr, u32 end_addr, void *ptr, int is_sub)\r
121 {\r
122   uptr *r8map, *r16map, *w8map, *w16map;\r
123   uptr addr = (uptr)ptr;\r
124   int shift = M68K_MEM_SHIFT;\r
125   int i;\r
126 \r
127   if (!is_sub) {\r
128     r8map = m68k_read8_map;\r
129     r16map = m68k_read16_map;\r
130     w8map = m68k_write8_map;\r
131     w16map = m68k_write16_map;\r
132   } else {\r
133     r8map = s68k_read8_map;\r
134     r16map = s68k_read16_map;\r
135     w8map = s68k_write8_map;\r
136     w16map = s68k_write16_map;\r
137   }\r
138 \r
139   addr -= start_addr;\r
140   addr >>= 1;\r
141   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
142     r8map[i] = r16map[i] = w8map[i] = w16map[i] = addr;\r
143 #ifdef EMU_F68K\r
144   // setup FAME fetchmap\r
145   {\r
146     M68K_CONTEXT *ctx = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;\r
147     int shiftout = 24 - FAMEC_FETCHBITS;\r
148     i = start_addr >> shiftout;\r
149     addr = (uptr)ptr - (i << shiftout);\r
150     for (; i <= (end_addr >> shiftout); i++)\r
151       ctx->Fetch[i] = addr;\r
152   }\r
153 #endif\r
154 }\r
155 \r
156 void cpu68k_map_read_funcs(u32 start_addr, u32 end_addr, u32 (*r8)(u32), u32 (*r16)(u32), int is_sub)\r
157 {\r
158   uptr *r8map, *r16map;\r
159   uptr ar8 = (uptr)r8, ar16 = (uptr)r16;\r
160   int shift = M68K_MEM_SHIFT;\r
161   int i;\r
162 \r
163   if (!is_sub) {\r
164     r8map = m68k_read8_map;\r
165     r16map = m68k_read16_map;\r
166   } else {\r
167     r8map = s68k_read8_map;\r
168     r16map = s68k_read16_map;\r
169   }\r
170 \r
171   ar8 = (ar8 >> 1 ) | MAP_FLAG;\r
172   ar16 = (ar16 >> 1 ) | MAP_FLAG;\r
173   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
174     r8map[i] = ar8, r16map[i] = ar16;\r
175 }\r
176 \r
177 void cpu68k_map_all_funcs(u32 start_addr, u32 end_addr, u32 (*r8)(u32), u32 (*r16)(u32), void (*w8)(u32, u32), void (*w16)(u32, u32), int is_sub)\r
178 {\r
179   uptr *r8map, *r16map, *w8map, *w16map;\r
180   uptr ar8 = (uptr)r8, ar16 = (uptr)r16;\r
181   uptr aw8 = (uptr)w8, aw16 = (uptr)w16;\r
182   int shift = M68K_MEM_SHIFT;\r
183   int i;\r
184 \r
185   if (!is_sub) {\r
186     r8map = m68k_read8_map;\r
187     r16map = m68k_read16_map;\r
188     w8map = m68k_write8_map;\r
189     w16map = m68k_write16_map;\r
190   } else {\r
191     r8map = s68k_read8_map;\r
192     r16map = s68k_read16_map;\r
193     w8map = s68k_write8_map;\r
194     w16map = s68k_write16_map;\r
195   }\r
196 \r
197   ar8 = (ar8 >> 1 ) | MAP_FLAG;\r
198   ar16 = (ar16 >> 1 ) | MAP_FLAG;\r
199   aw8 = (aw8 >> 1 ) | MAP_FLAG;\r
200   aw16 = (aw16 >> 1 ) | MAP_FLAG;\r
201   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
202     r8map[i] = ar8, r16map[i] = ar16, w8map[i] = aw8, w16map[i] = aw16;\r
203 }\r
204 \r
205 u32 PicoRead16_floating(u32 a)\r
206 {\r
207   // faking open bus\r
208   u16 d = (Pico.m.rotate += 0x41);\r
209   d ^= (d << 5) ^ (d << 8);\r
210   if ((a & 0xff0000) == 0xa10000) return d; // MegaCD pulldowns don't work here curiously\r
211   return (PicoIn.AHW & PAHW_MCD) ? 0x00 : d; // pulldown if MegaCD2 attached\r
212 }\r
213 \r
214 static u32 m68k_unmapped_read8(u32 a)\r
215 {\r
216   elprintf(EL_UIO, "m68k unmapped r8  [%06x] @%06x", a, SekPc);\r
217   return a < 0x400000 ? 0 : (u8)PicoRead16_floating(a);\r
218 }\r
219 \r
220 static u32 m68k_unmapped_read16(u32 a)\r
221 {\r
222   elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc);\r
223   return a < 0x400000 ? 0 : PicoRead16_floating(a);\r
224 }\r
225 \r
226 static void m68k_unmapped_write8(u32 a, u32 d)\r
227 {\r
228   elprintf(EL_UIO, "m68k unmapped w8  [%06x]   %02x @%06x", a, d & 0xff, SekPc);\r
229 }\r
230 \r
231 static void m68k_unmapped_write16(u32 a, u32 d)\r
232 {\r
233   elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);\r
234 }\r
235 \r
236 void m68k_map_unmap(u32 start_addr, u32 end_addr)\r
237 {\r
238 #ifdef __clang__\r
239   // workaround bug (segfault) in \r
240   // Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn)\r
241   volatile \r
242 #endif\r
243   uptr addr;\r
244   int shift = M68K_MEM_SHIFT;\r
245   int i;\r
246 \r
247   addr = (uptr)m68k_unmapped_read8;\r
248   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
249     m68k_read8_map[i] = (addr >> 1) | MAP_FLAG;\r
250 \r
251   addr = (uptr)m68k_unmapped_read16;\r
252   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
253     m68k_read16_map[i] = (addr >> 1) | MAP_FLAG;\r
254 \r
255   addr = (uptr)m68k_unmapped_write8;\r
256   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
257     m68k_write8_map[i] = (addr >> 1) | MAP_FLAG;\r
258 \r
259   addr = (uptr)m68k_unmapped_write16;\r
260   for (i = start_addr >> shift; i <= end_addr >> shift; i++)\r
261     m68k_write16_map[i] = (addr >> 1) | MAP_FLAG;\r
262 }\r
263 \r
264 #ifndef _ASM_MEMORY_C\r
265 MAKE_68K_READ8(m68k_read8, m68k_read8_map)\r
266 MAKE_68K_READ16(m68k_read16, m68k_read16_map)\r
267 MAKE_68K_READ32(m68k_read32, m68k_read16_map)\r
268 MAKE_68K_WRITE8(m68k_write8, m68k_write8_map)\r
269 MAKE_68K_WRITE16(m68k_write16, m68k_write16_map)\r
270 MAKE_68K_WRITE32(m68k_write32, m68k_write16_map)\r
271 #endif\r
272 \r
273 // -----------------------------------------------------------------\r
274 \r
275 static u32 ym2612_read_local_68k(void);\r
276 static int ym2612_write_local(u32 a, u32 d, int is_from_z80);\r
277 static void z80_mem_setup(void);\r
278 \r
279 #ifdef _ASM_MEMORY_C\r
280 u32 PicoRead8_sram(u32 a);\r
281 u32 PicoRead16_sram(u32 a);\r
282 #endif\r
283 \r
284 #ifdef EMU_CORE_DEBUG\r
285 u32 lastread_a, lastread_d[16]={0,}, lastwrite_cyc_d[16]={0,}, lastwrite_mus_d[16]={0,};\r
286 int lrp_cyc=0, lrp_mus=0, lwp_cyc=0, lwp_mus=0;\r
287 extern unsigned int ppop;\r
288 #endif\r
289 \r
290 #ifdef IO_STATS\r
291 void log_io(unsigned int addr, int bits, int rw);\r
292 #elif defined(_MSC_VER)\r
293 #define log_io\r
294 #else\r
295 #define log_io(...)\r
296 #endif\r
297 \r
298 #if defined(EMU_C68K)\r
299 u32 cyclone_crashed(u32 pc, struct Cyclone *context)\r
300 {\r
301     // check for underlying ROM, in case of on-cart hw overlaying part of ROM\r
302     // NB assumes code isn't executed from the overlay, but I've never seen this\r
303     u32 pc24 = pc & 0xffffff;\r
304     if (pc24 >= Pico.romsize) {\r
305       // no ROM, so it's probably an illegal access\r
306       pc24 = Pico.romsize;\r
307       elprintf(EL_STATUS|EL_ANOMALY, "%c68k crash detected @ %06x",\r
308         context == &PicoCpuCM68k ? 'm' : 's', pc);\r
309     }\r
310 \r
311     context->membase = (u32)Pico.rom;\r
312     context->pc = (u32)Pico.rom + pc24;\r
313 \r
314     return context->pc;\r
315 }\r
316 #endif\r
317 \r
318 // -----------------------------------------------------------------\r
319 // memmap helpers\r
320 \r
321 static u32 read_pad_3btn(int i, u32 out_bits)\r
322 {\r
323   u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDU\r
324   u32 value;\r
325 \r
326   if (out_bits & 0x40) // TH\r
327     value = pad & 0x3f;                      // ?1CB RLDU\r
328   else\r
329     value = ((pad & 0xc0) >> 2) | (pad & 3); // ?0SA 00DU\r
330 \r
331   value |= out_bits & 0x40;\r
332   return value;\r
333 }\r
334 \r
335 static u32 read_pad_6btn(int i, u32 out_bits)\r
336 {\r
337   u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDU\r
338   int phase = Pico.m.padTHPhase[i];\r
339   u32 value;\r
340 \r
341   if (phase == 2 && !(out_bits & 0x40)) {\r
342     value = (pad & 0xc0) >> 2;                   // ?0SA 0000\r
343     goto out;\r
344   }\r
345   else if (phase == 3) {\r
346     if (out_bits & 0x40)\r
347       value = (pad & 0x30) | ((pad >> 8) & 0xf); // ?1CB MXYZ\r
348     else\r
349       value = ((pad & 0xc0) >> 2) | 0x0f;        // ?0SA 1111\r
350     goto out;\r
351   }\r
352 \r
353   if (out_bits & 0x40) // TH\r
354     value = pad & 0x3f;                          // ?1CB RLDU\r
355   else\r
356     value = ((pad & 0xc0) >> 2) | (pad & 3);     // ?0SA 00DU\r
357 \r
358 out:\r
359   value |= out_bits & 0x40;\r
360   return value;\r
361 }\r
362 \r
363 static u32 read_pad_team(int i, u32 out_bits)\r
364 {\r
365   u32 pad;\r
366   int phase = Pico.m.padTHPhase[i];\r
367   u32 value;\r
368 \r
369   switch (phase) {\r
370   case 0:\r
371     value = 0x03;\r
372     break;\r
373   case 1:\r
374     value = 0x0f;\r
375     break;\r
376   case 4: case 5: case 6: case 7: // controller IDs, all 3 btn for now\r
377     value = 0x00;\r
378     break;\r
379   case 8: case 10: case 12: case 14:\r
380     pad = ~PicoIn.padInt[(phase-8) >> 1];\r
381     value = pad & 0x0f;                          // ?x?x RLDU\r
382     break;\r
383   case 9: case 11: case 13: case 15:\r
384     pad = ~PicoIn.padInt[(phase-8) >> 1];\r
385     value = (pad & 0xf0) >>  4;                  // ?x?x SACB\r
386     break;\r
387   default:\r
388     value = 0;\r
389     break;\r
390   }\r
391 \r
392   value |= (out_bits & 0x40) | ((out_bits & 0x20)>>1);\r
393   return value;\r
394 }\r
395 \r
396 static u32 read_pad_4way(int i, u32 out_bits)\r
397 {\r
398   u32 pad = (PicoMem.ioports[2] & 0x70) >> 4;\r
399   u32 value = 0;\r
400 \r
401   if (i == 0 && pad <= 3)\r
402     value = read_pad_3btn(pad, out_bits);\r
403 \r
404   value |= (out_bits & 0x40);\r
405   return value;\r
406 }\r
407 \r
408 static u32 read_pad_mouse(int i, u32 out_bits)\r
409 {\r
410   int phase = Pico.m.padTHPhase[i];\r
411   u32 value;\r
412 \r
413   int x = PicoIn.mouseInt[0] - PicoIn.mouseInt[2];\r
414   int y = PicoIn.mouseInt[3] - PicoIn.mouseInt[1];\r
415 \r
416   switch (phase) {\r
417   case 0:\r
418     value = 0x00;\r
419     break;\r
420   case 1:\r
421     value = 0x0b;\r
422     break;\r
423   case 2:\r
424     value = 0x0f;\r
425     // store last read values for x,y difference calculation\r
426     PicoIn.mouseInt[2] = PicoIn.mouseInt[0];\r
427     PicoIn.mouseInt[3] = PicoIn.mouseInt[1];\r
428     break;\r
429   case 3:\r
430     value = 0x0f;\r
431     // latch current mouse position during readout\r
432     PicoIn.mouseInt[0] = PicoIn.mouse[0];\r
433     PicoIn.mouseInt[1] = PicoIn.mouse[1];\r
434     break;\r
435   case 4: // xxxx OOSS, OO = y,x overflow, SS = y,x sign bits\r
436     value = (x<0) | ((y<0)<<1) | ((x<-255||x>255)<<2) | ((y<-255||y>255)<<3);\r
437     break;\r
438   case 5:\r
439     value = (PicoIn.padInt[i] & 0xf0) >> 4;      // SMRL, mapped from SACB\r
440     break;\r
441   case 6: // high nibble of x\r
442     value = (x>>4) & 0xf;\r
443     break;\r
444   case 7: // low nibble of x\r
445     value = x & 0xf;\r
446     break;\r
447   case 8: // high nibble of y\r
448     value = (y>>4) & 0xf;\r
449     break;\r
450   case 9: // low nibble of y\r
451   default: // also sent on all later phases\r
452     value = y & 0xf;\r
453     break;\r
454   }\r
455 \r
456   value |= (out_bits & 0x40) | ((out_bits & 0x20)>>1);\r
457   return value;\r
458 }\r
459 \r
460 static u32 read_nothing(int i, u32 out_bits)\r
461 {\r
462   return 0xff;\r
463 }\r
464 \r
465 typedef u32 (port_read_func)(int index, u32 out_bits);\r
466 \r
467 static port_read_func *port_readers[3] = {\r
468   read_pad_3btn,\r
469   read_pad_3btn,\r
470   read_nothing\r
471 };\r
472 \r
473 static int padTHLatency[3];\r
474 static int padTLLatency[3];\r
475 \r
476 static NOINLINE u32 port_read(int i)\r
477 {\r
478   u32 data_reg = PicoMem.ioports[i + 1];\r
479   u32 ctrl_reg = PicoMem.ioports[i + 4] | 0x80;\r
480   u32 in, out;\r
481 \r
482   out = data_reg & ctrl_reg;\r
483 \r
484   // pull-ups: should be 0x7f, but Decap Attack has a bug where it temp.\r
485   // disables output before doing TH-low read, so emulate RC filter for TH.\r
486   // Decap Attack reportedly doesn't work on Nomad but works on most\r
487   // other MD revisions (different pull-up strength?).\r
488   u32 mask = 0x3f;\r
489   if (CYCLES_GE(SekCyclesDone(), padTHLatency[i])) {\r
490     mask |= 0x40;\r
491     padTHLatency[i] = SekCyclesDone();\r
492   }\r
493   out |= mask & ~ctrl_reg;\r
494 \r
495   in = port_readers[i](i, out);\r
496 \r
497   // Sega mouse uses the TL/TR lines for req/ack. For buggy drivers, make sure\r
498   // there's some delay before ack is sent by taking over the new TL line level\r
499   if (CYCLES_GE(SekCyclesDone(), padTLLatency[i]))\r
500     padTLLatency[i] = SekCyclesDone();\r
501   else\r
502     in ^= 0x10; // TL\r
503 \r
504   return (in & ~ctrl_reg) | (data_reg & ctrl_reg);\r
505 }\r
506 \r
507 // pad export for J-Cart\r
508 u32 PicoReadPad(int i, u32 out_bits)\r
509 {\r
510   return read_pad_3btn(i, out_bits);\r
511 }\r
512 \r
513 void PicoSetInputDevice(int port, enum input_device device)\r
514 {\r
515   port_read_func *func;\r
516 \r
517   if (port < 0 || port > 2)\r
518     return;\r
519 \r
520   if (port == 1 && port_readers[0] == read_pad_team)\r
521     func = read_nothing;\r
522 \r
523   else switch (device) {\r
524   case PICO_INPUT_PAD_3BTN:\r
525     func = read_pad_3btn;\r
526     break;\r
527 \r
528   case PICO_INPUT_PAD_6BTN:\r
529     func = read_pad_6btn;\r
530     break;\r
531 \r
532   case PICO_INPUT_PAD_TEAM:\r
533     func = read_pad_team;\r
534     break;\r
535 \r
536   case PICO_INPUT_PAD_4WAY:\r
537     func = read_pad_4way;\r
538     break;\r
539 \r
540   case PICO_INPUT_MOUSE:\r
541     func = read_pad_mouse;\r
542     break;\r
543 \r
544   default:\r
545     func = read_nothing;\r
546     break;\r
547   }\r
548 \r
549   port_readers[port] = func;\r
550 }\r
551 \r
552 NOINLINE u32 io_ports_read(u32 a)\r
553 {\r
554   u32 d;\r
555   a = (a>>1) & 0xf;\r
556   switch (a) {\r
557     case 0:  d = Pico.m.hardware; break; // Hardware value (Version register)\r
558     case 1:  d = port_read(0); break;\r
559     case 2:  d = port_read(1); break;\r
560     case 3:  d = port_read(2); break;\r
561     default: d = PicoMem.ioports[a]; break; // IO ports can be used as RAM\r
562   }\r
563   return d;\r
564 }\r
565 \r
566 NOINLINE void io_ports_write(u32 a, u32 d)\r
567 {\r
568   a = (a>>1) & 0xf;\r
569 \r
570   // for some controllers, changing TH/TR changes controller state\r
571   if (1 <= a && a <= 2)\r
572   {\r
573     Pico.m.padDelay[a - 1] = 0;\r
574     if (port_readers[a - 1] == read_pad_team) {\r
575       if (d & 0x40)\r
576         Pico.m.padTHPhase[a - 1] = 0;\r
577       else if ((d^PicoMem.ioports[a]) & 0x60)\r
578         Pico.m.padTHPhase[a - 1]++;\r
579     } else if (port_readers[0] == read_pad_4way) {\r
580       if (a == 2 && ((PicoMem.ioports[a] ^ d) & 0x70))\r
581         Pico.m.padTHPhase[0] = 0;\r
582       if (a == 1 && !(PicoMem.ioports[a] & 0x40) && (d & 0x40))\r
583         Pico.m.padTHPhase[0]++;\r
584     } else if (port_readers[a - 1] == read_pad_mouse) {\r
585       if ((d^PicoMem.ioports[a]) & 0x20) {\r
586         if (Pico.m.padTHPhase[a - 1]) { // in readout?\r
587           padTLLatency[a - 1] = SekCyclesDone() + 100;\r
588           Pico.m.padTHPhase[a - 1]++;\r
589         } else // in ID cycle\r
590           padTLLatency[a - 1] = SekCyclesDone() + 25; // Cannon Fodder\r
591       }\r
592       if ((d^PicoMem.ioports[a]) & 0x40) {\r
593         // 1->0 transition starts the readout protocol\r
594         Pico.m.padTHPhase[a - 1] = !(d & 0x40);\r
595       }\r
596     } else if (!(PicoMem.ioports[a] & 0x40) && (d & 0x40))\r
597       Pico.m.padTHPhase[a - 1]++;\r
598   }\r
599 \r
600   // after switching TH to input there's a latency before the pullup value is \r
601   // read back as input (see Decap Attack, not in Samurai Showdown, 32x WWF Raw)\r
602   if (4 <= a && a <= 5) {\r
603     if ((PicoMem.ioports[a] & 0x40) && !(d & 0x40) && !(PicoMem.ioports[a - 3] & 0x40))\r
604       // latency after switching to input and output was low\r
605       padTHLatency[a - 4] = SekCyclesDone() + 25;\r
606   }\r
607 \r
608   // certain IO ports can be used as RAM\r
609   PicoMem.ioports[a] = d;\r
610 }\r
611 \r
612 int io_ports_pack(void *buf, size_t size)\r
613 {\r
614   size_t b, i;\r
615   memcpy(buf, PicoMem.ioports, (b = sizeof(PicoMem.ioports)));\r
616   for (i = 0; i < ARRAY_SIZE(Pico.m.padTHPhase); i++)\r
617     save_u8_(buf, &b, Pico.m.padTHPhase[i]);\r
618   for (i = 0; i < ARRAY_SIZE(Pico.m.padDelay); i++)\r
619     save_u8_(buf, &b, Pico.m.padDelay[i]);\r
620   for (i = 0; i < ARRAY_SIZE(padTHLatency); i++) {\r
621     save_s32(buf, &b, padTHLatency[i]);\r
622     save_s32(buf, &b, padTLLatency[i]);\r
623   }\r
624   for (i = 0; i < 4; i++)\r
625     save_u16(buf, &b, PicoIn.padInt[i]);\r
626   for (i = 0; i < 4; i++)\r
627     save_s32(buf, &b, PicoIn.mouseInt[i]);\r
628   assert(b <= size);\r
629   return b;\r
630 }\r
631 \r
632 void io_ports_unpack(const void *buf, size_t size)\r
633 {\r
634   size_t b, i;\r
635   memcpy(PicoMem.ioports, buf, (b = sizeof(PicoMem.ioports)));\r
636   for (i = 0; i < ARRAY_SIZE(Pico.m.padTHPhase); i++)\r
637     Pico.m.padTHPhase[i] = load_u8_(buf, &b);\r
638   for (i = 0; i < ARRAY_SIZE(Pico.m.padDelay); i++)\r
639     Pico.m.padDelay[i] = load_u8_(buf, &b);\r
640   for (i = 0; i < ARRAY_SIZE(padTHLatency); i++) {\r
641     padTHLatency[i] = load_s32(buf, &b);\r
642     padTLLatency[i] = load_s32(buf, &b);\r
643   }\r
644   for (i = 0; i < 4; i++)\r
645     PicoIn.padInt[i] = load_u16(buf, &b);\r
646   for (i = 0; i < 4; i++)\r
647     PicoIn.mouseInt[i] = load_s32(buf, &b);\r
648   assert(b <= size);\r
649 }\r
650 \r
651 static int z80_cycles_from_68k(void)\r
652 {\r
653   int m68k_cnt = SekCyclesDone() - Pico.t.m68c_frame_start;\r
654   return cycles_68k_to_z80(m68k_cnt);\r
655 }\r
656 \r
657 void NOINLINE ctl_write_z80busreq(u32 d)\r
658 {\r
659   d&=1; d^=1;\r
660   elprintf(EL_BUSREQ, "set_zrun: %i->%i [%u] @%06x", Pico.m.z80Run, d, SekCyclesDone(), SekPc);\r
661   if (d ^ Pico.m.z80Run)\r
662   {\r
663     if (d)\r
664     {\r
665       Pico.t.z80c_aim = Pico.t.z80c_cnt = z80_cycles_from_68k() + 2;\r
666       Pico.t.z80c_cnt += Pico.t.z80_busdelay >> 8;\r
667       Pico.t.z80_busdelay &= 0xff;\r
668     }\r
669     else\r
670     {\r
671       if ((PicoIn.opt & POPT_EN_Z80) && !Pico.m.z80_reset) {\r
672         // Z80 grants bus after the current M cycle, even within an insn\r
673         // simulate this by accumulating the last insn overhang in busdelay\r
674         unsigned granted;\r
675         pprof_start(m68k);\r
676         PicoSyncZ80(SekCyclesDone());\r
677         pprof_end_sub(m68k);\r
678         granted = Pico.t.z80c_aim + 6; // M cycle is 3-6 cycles \r
679         Pico.t.z80_busdelay += (Pico.t.z80c_cnt - granted) << 8;\r
680         Pico.t.z80c_cnt = granted;\r
681       }\r
682     }\r
683     Pico.m.z80Run = d;\r
684   }\r
685 }\r
686 \r
687 void NOINLINE ctl_write_z80reset(u32 d)\r
688 {\r
689   d&=1; d^=1;\r
690   elprintf(EL_BUSREQ, "set_zreset: %i->%i [%u] @%06x", Pico.m.z80_reset, d, SekCyclesDone(), SekPc);\r
691   if (d ^ Pico.m.z80_reset)\r
692   {\r
693     if (d)\r
694     {\r
695       if ((PicoIn.opt & POPT_EN_Z80) && Pico.m.z80Run) {\r
696         pprof_start(m68k);\r
697         PicoSyncZ80(SekCyclesDone());\r
698         pprof_end_sub(m68k);\r
699       }\r
700       Pico.t.z80_busdelay &= 0xff; // also resets bus request\r
701       YM2612ResetChip();\r
702       timers_reset();\r
703     }\r
704     else\r
705     {\r
706       Pico.t.z80c_aim = Pico.t.z80c_cnt = z80_cycles_from_68k() + 2;\r
707       z80_reset();\r
708     }\r
709     Pico.m.z80_reset = d;\r
710   }\r
711 }\r
712 \r
713 static void psg_write_68k(u32 d)\r
714 {\r
715   PsndDoPSG(z80_cycles_from_68k());\r
716   SN76496Write(d);\r
717 }\r
718 \r
719 static void psg_write_z80(u32 d)\r
720 {\r
721   PsndDoPSG(z80_cyclesDone());\r
722   SN76496Write(d);\r
723 }\r
724 \r
725 // -----------------------------------------------------------------\r
726 \r
727 #ifndef _ASM_MEMORY_C\r
728 \r
729 // cart (save) RAM area (usually 0x200000 - ...)\r
730 static u32 PicoRead8_sram(u32 a)\r
731 {\r
732   u32 d;\r
733   if (Pico.sv.start <= a && a <= Pico.sv.end && (Pico.m.sram_reg & SRR_MAPPED))\r
734   {\r
735     if (Pico.sv.flags & SRF_EEPROM) {\r
736       d = EEPROM_read();\r
737       if (!(a & 1))\r
738         d >>= 8;\r
739       d &= 0xff;\r
740     } else\r
741       d = *(u8 *)(Pico.sv.data - Pico.sv.start + a);\r
742     elprintf(EL_SRAMIO, "sram r8  [%06x]   %02x @ %06x", a, d, SekPc);\r
743     return d;\r
744   }\r
745 \r
746   // XXX: this is banking unfriendly\r
747   if (a < Pico.romsize)\r
748     return Pico.rom[MEM_BE2(a)];\r
749   \r
750   return m68k_unmapped_read8(a);\r
751 }\r
752 \r
753 static u32 PicoRead16_sram(u32 a)\r
754 {\r
755   u32 d;\r
756   if (Pico.sv.start <= a && a <= Pico.sv.end && (Pico.m.sram_reg & SRR_MAPPED))\r
757   {\r
758     if (Pico.sv.flags & SRF_EEPROM)\r
759       d = EEPROM_read();\r
760     else {\r
761       u8 *pm = (u8 *)(Pico.sv.data - Pico.sv.start + a);\r
762       d  = pm[0] << 8;\r
763       d |= pm[1];\r
764     }\r
765     elprintf(EL_SRAMIO, "sram r16 [%06x] %04x @ %06x", a, d, SekPc);\r
766     return d;\r
767   }\r
768 \r
769   if (a < Pico.romsize)\r
770     return *(u16 *)(Pico.rom + a);\r
771 \r
772   return m68k_unmapped_read16(a);\r
773 }\r
774 \r
775 #endif // _ASM_MEMORY_C\r
776 \r
777 static void PicoWrite8_sram(u32 a, u32 d)\r
778 {\r
779   if (a > Pico.sv.end || a < Pico.sv.start || !(Pico.m.sram_reg & SRR_MAPPED)) {\r
780     m68k_unmapped_write8(a, d);\r
781     return;\r
782   }\r
783 \r
784   elprintf(EL_SRAMIO, "sram w8  [%06x]   %02x @ %06x", a, d & 0xff, SekPc);\r
785   if (Pico.sv.flags & SRF_EEPROM)\r
786   {\r
787     EEPROM_write8(a, d);\r
788   }\r
789   else {\r
790     u8 *pm = (u8 *)(Pico.sv.data - Pico.sv.start + a);\r
791     if (*pm != (u8)d) {\r
792       Pico.sv.changed = 1;\r
793       *pm = (u8)d;\r
794     }\r
795   }\r
796 }\r
797 \r
798 static void PicoWrite16_sram(u32 a, u32 d)\r
799 {\r
800   if (a > Pico.sv.end || a < Pico.sv.start || !(Pico.m.sram_reg & SRR_MAPPED)) {\r
801     m68k_unmapped_write16(a, d);\r
802     return;\r
803   }\r
804 \r
805   elprintf(EL_SRAMIO, "sram w16 [%06x] %04x @ %06x", a, d & 0xffff, SekPc);\r
806   if (Pico.sv.flags & SRF_EEPROM)\r
807   {\r
808     EEPROM_write16(d);\r
809   }\r
810   else {\r
811     u8 *pm = (u8 *)(Pico.sv.data - Pico.sv.start + a);\r
812     if (pm[0] != (u8)(d >> 8)) {\r
813       Pico.sv.changed = 1;\r
814       pm[0] = (u8)(d >> 8);\r
815     }\r
816     if (pm[1] != (u8)d) {\r
817       Pico.sv.changed = 1;\r
818       pm[1] = (u8)d;\r
819     }\r
820   }\r
821 }\r
822 \r
823 // z80 area (0xa00000 - 0xa0ffff)\r
824 // TODO: verify mirrors VDP and bank reg (bank area mirroring verified)\r
825 static u32 PicoRead8_z80(u32 a)\r
826 {\r
827   u32 d;\r
828   if ((Pico.m.z80Run | Pico.m.z80_reset | (z80_cycles_from_68k() < Pico.t.z80c_cnt)) &&\r
829       !(PicoIn.quirks & PQUIRK_NO_Z80_BUS_LOCK)) {\r
830     elprintf(EL_ANOMALY, "68k z80 read with no bus! [%06x] @ %06x", a, SekPc);\r
831     return (u8)PicoRead16_floating(a);\r
832   }\r
833   SekCyclesBurnRun(1);\r
834 \r
835   if ((a & 0x4000) == 0x0000) {\r
836     d = PicoMem.zram[a & 0x1fff];\r
837   } else if ((a & 0x6000) == 0x4000) // 0x4000-0x5fff\r
838     d = ym2612_read_local_68k(); \r
839   else {\r
840     elprintf(EL_UIO|EL_ANOMALY, "68k bad read [%06x] @%06x", a, SekPc);\r
841     d = (u8)PicoRead16_floating(a);\r
842   }\r
843   return d;\r
844 }\r
845 \r
846 static u32 PicoRead16_z80(u32 a)\r
847 {\r
848   u32 d = PicoRead8_z80(a);\r
849   return d | (d << 8);\r
850 }\r
851 \r
852 static void PicoWrite8_z80(u32 a, u32 d)\r
853 {\r
854   if ((Pico.m.z80Run | Pico.m.z80_reset) && !(PicoIn.quirks & PQUIRK_NO_Z80_BUS_LOCK)) {\r
855     // verified on real hw\r
856     elprintf(EL_ANOMALY, "68k z80 write with no bus or reset! [%06x] %02x @ %06x", a, d&0xff, SekPc);\r
857     return;\r
858   }\r
859   SekCyclesBurnRun(1);\r
860 \r
861   if ((a & 0x4000) == 0x0000) { // z80 RAM\r
862     PicoMem.zram[a & 0x1fff] = (u8)d;\r
863     return;\r
864   }\r
865   if ((a & 0x6000) == 0x4000) { // FM Sound\r
866     if (PicoIn.opt & POPT_EN_FM)\r
867       ym2612_write_local(a & 3, d & 0xff, 0);\r
868     return;\r
869   }\r
870   // TODO: probably other VDP access too? Maybe more mirrors?\r
871   if ((a & 0x7ff9) == 0x7f11) { // PSG Sound\r
872     psg_write_68k(d);\r
873     return;\r
874   }\r
875   if ((a & 0x7f00) == 0x6000) // Z80 BANK register\r
876   {\r
877     Pico.m.z80_bank68k >>= 1;\r
878     Pico.m.z80_bank68k |= d << 8;\r
879     Pico.m.z80_bank68k &= 0x1ff; // 9 bits and filled in the new top one\r
880     elprintf(EL_Z80BNK, "z80 bank=%06x", Pico.m.z80_bank68k << 15);\r
881     return;\r
882   }\r
883   elprintf(EL_UIO|EL_ANOMALY, "68k bad write [%06x] %02x @ %06x", a, d&0xff, SekPc);\r
884 }\r
885 \r
886 static void PicoWrite16_z80(u32 a, u32 d)\r
887 {\r
888   // for RAM, only most significant byte is sent\r
889   // TODO: verify remaining accesses\r
890   PicoWrite8_z80(a, d >> 8);\r
891 }\r
892 \r
893 #ifndef _ASM_MEMORY_C\r
894 \r
895 // IO/control area (0xa10000 - 0xa1ffff)\r
896 u32 PicoRead8_io(u32 a)\r
897 {\r
898   u32 d;\r
899 \r
900   if ((a & 0xffe0) == 0x0000) { // I/O ports\r
901     d = io_ports_read(a);\r
902     goto end;\r
903   }\r
904 \r
905   if ((a & 0xfc00) == 0x1000) {\r
906     d = (u8)PicoRead16_floating(a);\r
907 \r
908     if ((a & 0xff01) == 0x1100) { // z80 busreq (verified)\r
909       // bit8 seems to be readable in this range\r
910       if (!(a & 1)) {\r
911         d &= ~0x01;\r
912         // Z80 ahead of 68K only if in BUSREQ, BUSACK only after 68K reached Z80\r
913         d |= (z80_cycles_from_68k() < Pico.t.z80c_cnt);\r
914         d |= (Pico.m.z80Run | Pico.m.z80_reset) & 1;\r
915         elprintf(EL_BUSREQ, "get_zrun: %02x [%u] @%06x", d, SekCyclesDone(), SekPc);\r
916       }\r
917     }\r
918     goto end;\r
919   }\r
920 \r
921   d = PicoRead8_32x(a);\r
922 \r
923 end:\r
924   return d;\r
925 }\r
926 \r
927 u32 PicoRead16_io(u32 a)\r
928 {\r
929   u32 d;\r
930 \r
931   if ((a & 0xffe0) == 0x0000) { // I/O ports\r
932     d = io_ports_read(a);\r
933     d |= d << 8;\r
934     goto end;\r
935   }\r
936 \r
937   // bit8 seems to be readable in this range\r
938   if ((a & 0xfc00) == 0x1000) {\r
939     d = PicoRead16_floating(a);\r
940 \r
941     if ((a & 0xff00) == 0x1100) { // z80 busreq\r
942       d &= ~0x0100;\r
943       d |= (z80_cycles_from_68k() < Pico.t.z80c_cnt) << 8;\r
944       d |= ((Pico.m.z80Run | Pico.m.z80_reset) & 1) << 8;\r
945       elprintf(EL_BUSREQ, "get_zrun: %04x [%u] @%06x", d, SekCyclesDone(), SekPc);\r
946     }\r
947     goto end;\r
948   }\r
949 \r
950   d = PicoRead16_32x(a);\r
951 \r
952 end:\r
953   return d;\r
954 }\r
955 \r
956 void PicoWrite8_io(u32 a, u32 d)\r
957 {\r
958   if ((a & 0xffe1) == 0x0001) { // I/O ports (verified: only LSB!)\r
959     io_ports_write(a, d);\r
960     return;\r
961   }\r
962   if ((a & 0xff01) == 0x1100) { // z80 busreq\r
963     ctl_write_z80busreq(d);\r
964     return;\r
965   }\r
966   if ((a & 0xff01) == 0x1200) { // z80 reset\r
967     ctl_write_z80reset(d);\r
968     return;\r
969   }\r
970   if (a == 0xa130f1) { // sram access register\r
971     elprintf(EL_SRAMIO, "sram reg=%02x", d);\r
972     Pico.m.sram_reg &= ~(SRR_MAPPED|SRR_READONLY);\r
973     Pico.m.sram_reg |= (u8)(d & 3);\r
974     return;\r
975   }\r
976   PicoWrite8_32x(a, d);\r
977 }\r
978 \r
979 void PicoWrite16_io(u32 a, u32 d)\r
980 {\r
981   if ((a & 0xffe0) == 0x0000) { // I/O ports (verified: only LSB!)\r
982     io_ports_write(a, d);\r
983     return;\r
984   }\r
985   if ((a & 0xff00) == 0x1100) { // z80 busreq\r
986     ctl_write_z80busreq(d >> 8);\r
987     return;\r
988   }\r
989   if ((a & 0xff00) == 0x1200) { // z80 reset\r
990     ctl_write_z80reset(d >> 8);\r
991     return;\r
992   }\r
993   if (a == 0xa130f0) { // sram access register\r
994     elprintf(EL_SRAMIO, "sram reg=%02x", d);\r
995     Pico.m.sram_reg &= ~(SRR_MAPPED|SRR_READONLY);\r
996     Pico.m.sram_reg |= (u8)(d & 3);\r
997     return;\r
998   }\r
999   PicoWrite16_32x(a, d);\r
1000 }\r
1001 \r
1002 #endif // _ASM_MEMORY_C\r
1003 \r
1004 // VDP area (0xc00000 - 0xdfffff)\r
1005 // TODO: verify if lower byte goes to PSG on word writes\r
1006 u32 PicoRead8_vdp(u32 a)\r
1007 {\r
1008   u32 d;\r
1009   if ((a & 0x00f0) == 0x0000) {\r
1010     switch (a & 0x0d)\r
1011     {\r
1012       case 0x00: d = PicoVideoRead8DataH(0); break;\r
1013       case 0x01: d = PicoVideoRead8DataL(0); break;\r
1014       case 0x04: d = PicoVideoRead8CtlH(0); break;\r
1015       case 0x05: d = PicoVideoRead8CtlL(0); break;\r
1016       case 0x08:\r
1017       case 0x0c: d = PicoVideoRead8HV_H(0); break;\r
1018       case 0x09:\r
1019       case 0x0d: d = PicoVideoRead8HV_L(0); break;\r
1020       default:   d = (u8)PicoRead16_floating(a); break;\r
1021     }\r
1022   } else {\r
1023     elprintf(EL_UIO|EL_ANOMALY, "68k bad read [%06x] @%06x", a, SekPc);\r
1024     d = (u8)PicoRead16_floating(a);\r
1025   }\r
1026   return d;\r
1027 }\r
1028 \r
1029 static u32 PicoRead16_vdp(u32 a)\r
1030 {\r
1031   if ((a & 0x00e0) == 0x0000)\r
1032     return PicoVideoRead(a);\r
1033 \r
1034   elprintf(EL_UIO|EL_ANOMALY, "68k bad read [%06x] @%06x", a, SekPc);\r
1035   return 0;\r
1036 }\r
1037 \r
1038 static void PicoWrite8_vdp(u32 a, u32 d)\r
1039 {\r
1040   if ((a & 0x00f9) == 0x0011) { // PSG Sound\r
1041     psg_write_68k(d);\r
1042     return;\r
1043   }\r
1044   if ((a & 0x00e0) == 0x0000) {\r
1045     d &= 0xff;\r
1046     PicoVideoWrite(a, d | (d << 8));\r
1047     return;\r
1048   }\r
1049 \r
1050   elprintf(EL_UIO|EL_ANOMALY, "68k bad write [%06x] %02x @%06x", a, d & 0xff, SekPc);\r
1051 }\r
1052 \r
1053 static void PicoWrite16_vdp(u32 a, u32 d)\r
1054 {\r
1055   if ((a & 0x00f9) == 0x0010) { // PSG Sound\r
1056     psg_write_68k(d);\r
1057     return;\r
1058   }\r
1059   if ((a & 0x00e0) == 0x0000) {\r
1060     PicoVideoWrite(a, d);\r
1061     return;\r
1062   }\r
1063 \r
1064   elprintf(EL_UIO|EL_ANOMALY, "68k bad write [%06x] %04x @%06x", a, d & 0xffff, SekPc);\r
1065 }\r
1066 \r
1067 // -----------------------------------------------------------------\r
1068 \r
1069 #ifdef EMU_M68K\r
1070 static void m68k_mem_setup(void);\r
1071 #endif\r
1072 \r
1073 PICO_INTERNAL void PicoMemSetup(void)\r
1074 {\r
1075   int mask, rs, sstart, a;\r
1076 \r
1077   // setup the memory map\r
1078   cpu68k_map_set(m68k_read8_map,   0x000000, 0xffffff, m68k_unmapped_read8, 1);\r
1079   cpu68k_map_set(m68k_read16_map,  0x000000, 0xffffff, m68k_unmapped_read16, 1);\r
1080   cpu68k_map_set(m68k_write8_map,  0x000000, 0xffffff, m68k_unmapped_write8, 1);\r
1081   cpu68k_map_set(m68k_write16_map, 0x000000, 0xffffff, m68k_unmapped_write16, 1);\r
1082 \r
1083   // ROM\r
1084   // align to bank size. We know ROM loader allocated enough for this\r
1085   mask = (1 << M68K_MEM_SHIFT) - 1;\r
1086   rs = (Pico.romsize + mask) & ~mask;\r
1087   if (rs > 0xa00000) rs = 0xa00000; // max cartridge area\r
1088   if (rs) {\r
1089     cpu68k_map_set(m68k_read8_map,  0x000000, rs - 1, Pico.rom, 0);\r
1090     cpu68k_map_set(m68k_read16_map, 0x000000, rs - 1, Pico.rom, 0);\r
1091   }\r
1092 \r
1093   // Common case of on-cart (save) RAM, usually at 0x200000-...\r
1094   if ((Pico.sv.flags & SRF_ENABLED) && Pico.sv.data != NULL) {\r
1095     sstart = Pico.sv.start & ~mask;\r
1096     rs = Pico.sv.end - sstart;\r
1097     rs = (rs + mask) & ~mask;\r
1098     if (sstart + rs >= 0x1000000)\r
1099       rs = 0x1000000 - sstart;\r
1100     cpu68k_map_set(m68k_read8_map,   sstart, sstart + rs - 1, PicoRead8_sram, 1);\r
1101     cpu68k_map_set(m68k_read16_map,  sstart, sstart + rs - 1, PicoRead16_sram, 1);\r
1102     cpu68k_map_set(m68k_write8_map,  sstart, sstart + rs - 1, PicoWrite8_sram, 1);\r
1103     cpu68k_map_set(m68k_write16_map, sstart, sstart + rs - 1, PicoWrite16_sram, 1);\r
1104   }\r
1105 \r
1106   // Z80 region\r
1107   cpu68k_map_set(m68k_read8_map,   0xa00000, 0xa0ffff, PicoRead8_z80, 1);\r
1108   cpu68k_map_set(m68k_read16_map,  0xa00000, 0xa0ffff, PicoRead16_z80, 1);\r
1109   cpu68k_map_set(m68k_write8_map,  0xa00000, 0xa0ffff, PicoWrite8_z80, 1);\r
1110   cpu68k_map_set(m68k_write16_map, 0xa00000, 0xa0ffff, PicoWrite16_z80, 1);\r
1111 \r
1112   // IO/control region\r
1113   cpu68k_map_set(m68k_read8_map,   0xa10000, 0xa1ffff, PicoRead8_io, 1);\r
1114   cpu68k_map_set(m68k_read16_map,  0xa10000, 0xa1ffff, PicoRead16_io, 1);\r
1115   cpu68k_map_set(m68k_write8_map,  0xa10000, 0xa1ffff, PicoWrite8_io, 1);\r
1116   cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, PicoWrite16_io, 1);\r
1117 \r
1118   // VDP region\r
1119   for (a = 0xc00000; a < 0xe00000; a += 0x010000) {\r
1120     if ((a & 0xe700e0) != 0xc00000)\r
1121       continue;\r
1122     cpu68k_map_set(m68k_read8_map,   a, a + 0xffff, PicoRead8_vdp, 1);\r
1123     cpu68k_map_set(m68k_read16_map,  a, a + 0xffff, PicoRead16_vdp, 1);\r
1124     cpu68k_map_set(m68k_write8_map,  a, a + 0xffff, PicoWrite8_vdp, 1);\r
1125     cpu68k_map_set(m68k_write16_map, a, a + 0xffff, PicoWrite16_vdp, 1);\r
1126   }\r
1127 \r
1128   // RAM and it's mirrors\r
1129   for (a = 0xe00000; a < 0x1000000; a += 0x010000) {\r
1130     cpu68k_map_set(m68k_read8_map,   a, a + 0xffff, PicoMem.ram, 0);\r
1131     cpu68k_map_set(m68k_read16_map,  a, a + 0xffff, PicoMem.ram, 0);\r
1132     cpu68k_map_set(m68k_write8_map,  a, a + 0xffff, PicoMem.ram, 0);\r
1133     cpu68k_map_set(m68k_write16_map, a, a + 0xffff, PicoMem.ram, 0);\r
1134   }\r
1135 \r
1136   // Setup memory callbacks:\r
1137 #ifdef EMU_C68K\r
1138   PicoCpuCM68k.read8  = (void *)m68k_read8_map;\r
1139   PicoCpuCM68k.read16 = (void *)m68k_read16_map;\r
1140   PicoCpuCM68k.read32 = (void *)m68k_read16_map;\r
1141   PicoCpuCM68k.write8  = (void *)m68k_write8_map;\r
1142   PicoCpuCM68k.write16 = (void *)m68k_write16_map;\r
1143   PicoCpuCM68k.write32 = (void *)m68k_write16_map;\r
1144   PicoCpuCM68k.checkpc = NULL; /* unused */\r
1145   PicoCpuCM68k.fetch8  = NULL;\r
1146   PicoCpuCM68k.fetch16 = NULL;\r
1147   PicoCpuCM68k.fetch32 = NULL;\r
1148 #endif\r
1149 #ifdef EMU_F68K\r
1150   PicoCpuFM68k.read_byte  = (void *)m68k_read8;\r
1151   PicoCpuFM68k.read_word  = (void *)m68k_read16;\r
1152   PicoCpuFM68k.read_long  = (void *)m68k_read32;\r
1153   PicoCpuFM68k.write_byte = (void *)m68k_write8;\r
1154   PicoCpuFM68k.write_word = (void *)m68k_write16;\r
1155   PicoCpuFM68k.write_long = (void *)m68k_write32;\r
1156 #endif\r
1157 #ifdef EMU_M68K\r
1158   m68k_mem_setup();\r
1159 #endif\r
1160 \r
1161   z80_mem_setup();\r
1162 }\r
1163 \r
1164 #ifdef EMU_M68K\r
1165 unsigned int (*pm68k_read_memory_8) (unsigned int address) = NULL;\r
1166 unsigned int (*pm68k_read_memory_16)(unsigned int address) = NULL;\r
1167 unsigned int (*pm68k_read_memory_32)(unsigned int address) = NULL;\r
1168 void (*pm68k_write_memory_8) (unsigned int address, unsigned char  value) = NULL;\r
1169 void (*pm68k_write_memory_16)(unsigned int address, unsigned short value) = NULL;\r
1170 void (*pm68k_write_memory_32)(unsigned int address, unsigned int   value) = NULL;\r
1171 \r
1172 /* it appears that Musashi doesn't always mask the unused bits */\r
1173 unsigned int m68k_read_memory_8 (unsigned int address) { return pm68k_read_memory_8 (address) & 0xff; }\r
1174 unsigned int m68k_read_memory_16(unsigned int address) { return pm68k_read_memory_16(address) & 0xffff; }\r
1175 unsigned int m68k_read_memory_32(unsigned int address) { return pm68k_read_memory_32(address); }\r
1176 void m68k_write_memory_8 (unsigned int address, unsigned int value) { pm68k_write_memory_8 (address, (u8)value); }\r
1177 void m68k_write_memory_16(unsigned int address, unsigned int value) { pm68k_write_memory_16(address,(u16)value); }\r
1178 void m68k_write_memory_32(unsigned int address, unsigned int value) { pm68k_write_memory_32(address, value); }\r
1179 \r
1180 static void m68k_mem_setup(void)\r
1181 {\r
1182   pm68k_read_memory_8  = m68k_read8;\r
1183   pm68k_read_memory_16 = m68k_read16;\r
1184   pm68k_read_memory_32 = m68k_read32;\r
1185   pm68k_write_memory_8  = m68k_write8;\r
1186   pm68k_write_memory_16 = m68k_write16;\r
1187   pm68k_write_memory_32 = m68k_write32;\r
1188 }\r
1189 #endif // EMU_M68K\r
1190 \r
1191 \r
1192 // -----------------------------------------------------------------\r
1193 \r
1194 static int get_scanline(int is_from_z80)\r
1195 {\r
1196   if (is_from_z80) {\r
1197     // ugh... compute by dividing cycles since frame start by cycles per line\r
1198     // need some fractional resolution here, else there may be an extra line\r
1199     int cycles_line = cycles_68k_to_z80((unsigned)(488.5*256))+1; // cycles per line, Q8\r
1200     int cycles_z80 = (z80_cyclesLeft<0 ? Pico.t.z80c_aim:z80_cyclesDone())<<8;\r
1201     int cycles = cycles_line * Pico.t.z80_scanline;\r
1202     // approximation by multiplying with inverse\r
1203     if (cycles_z80 - cycles >= 4*cycles_line) {\r
1204       // compute 1/cycles_line, storing the result to avoid future dividing\r
1205       static int cycles_line_o, cycles_line_i;\r
1206       if (cycles_line_o != cycles_line)\r
1207         { cycles_line_o = cycles_line, cycles_line_i = (1<<22) / cycles_line; }\r
1208       // compute lines = diff/cycles_line = diff*(1/cycles_line)\r
1209       int lines = ((cycles_z80 - cycles) * cycles_line_i) >> 22;\r
1210       Pico.t.z80_scanline += lines, cycles += cycles_line * lines;\r
1211     }\r
1212     // handle any rounding leftover\r
1213     while (cycles_z80 - cycles >= cycles_line)\r
1214       Pico.t.z80_scanline ++, cycles += cycles_line;\r
1215     return Pico.t.z80_scanline;\r
1216   }\r
1217 \r
1218   return Pico.m.scanline;\r
1219 }\r
1220 \r
1221 #define ym2612_update_status(xcycles) \\r
1222   ym2612.OPN.ST.status &= ~0x80; \\r
1223   ym2612.OPN.ST.status |= (xcycles < Pico.t.ym2612_busy) * 0x80; \\r
1224   if (xcycles >= Pico.t.timer_a_next_oflow) \\r
1225     ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 1; \\r
1226   if (xcycles >= Pico.t.timer_b_next_oflow) \\r
1227     ym2612.OPN.ST.status |= (ym2612.OPN.ST.mode >> 2) & 2\r
1228 \r
1229 /* probably should not be in this file, but it's near related code here */\r
1230 void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new)\r
1231 {\r
1232   int xcycles = z80_cycles << 8;\r
1233 \r
1234   // update timer status\r
1235   ym2612_update_status(xcycles);\r
1236 \r
1237   // update timer a\r
1238   if (mode_old & 1)\r
1239     while (xcycles >= Pico.t.timer_a_next_oflow)\r
1240       Pico.t.timer_a_next_oflow += Pico.t.timer_a_step;\r
1241 \r
1242   // turning on/off\r
1243   if ((mode_old ^ mode_new) & 1)\r
1244   {\r
1245     if (mode_old & 1)\r
1246       Pico.t.timer_a_next_oflow = TIMER_NO_OFLOW;\r
1247     else {\r
1248       /* The internal tick of the YM2612 takes 144 clock cycles (with clock\r
1249        * being OSC/7), or 67.2 z80 cycles. Timers are run once each tick.\r
1250        * Starting a timer takes place at the next tick, so xcycles needs to be\r
1251        * rounded up to that: t = next tick# = (xcycles / TICK_ZCYCLES) + 1\r
1252        */\r
1253       unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1;\r
1254       Pico.t.timer_a_next_oflow = t*TIMER_A_TICK_ZCYCLES + Pico.t.timer_a_step;\r
1255     }\r
1256   }\r
1257 \r
1258   if (mode_new & 1)\r
1259     elprintf(EL_YMTIMER, "timer a upd to %i @ %i", Pico.t.timer_a_next_oflow>>8, z80_cycles);\r
1260 \r
1261   // update timer b\r
1262   if (mode_old & 2)\r
1263     while (xcycles >= Pico.t.timer_b_next_oflow)\r
1264       Pico.t.timer_b_next_oflow += Pico.t.timer_b_step;\r
1265 \r
1266   // turning on/off\r
1267   if ((mode_old ^ mode_new) & 2)\r
1268   {\r
1269     if (mode_old & 2)\r
1270       Pico.t.timer_b_next_oflow = TIMER_NO_OFLOW;\r
1271     else {\r
1272       /* timer b has a divider of 16 which runs in its own counter. It is not\r
1273        * reset by loading timer b. The first run of timer b after loading is\r
1274        * therefore shorter by up to 15 ticks.\r
1275        */\r
1276       unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1;\r
1277       int step = Pico.t.timer_b_step - TIMER_A_TICK_ZCYCLES*(t&15);\r
1278       Pico.t.timer_b_next_oflow = t*TIMER_A_TICK_ZCYCLES + step;\r
1279     }\r
1280   }\r
1281 \r
1282   if (mode_new & 2)\r
1283     elprintf(EL_YMTIMER, "timer b upd to %i @ %i", Pico.t.timer_b_next_oflow>>8, z80_cycles);\r
1284 }\r
1285 \r
1286 // ym2612 DAC and timer I/O handlers for z80\r
1287 static int ym2612_write_local(u32 a, u32 d, int is_from_z80)\r
1288 {\r
1289   int cycles = is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k();\r
1290   int addr;\r
1291 \r
1292   a &= 3;\r
1293   switch (a)\r
1294   {\r
1295     case 0: /* address port 0 */\r
1296     case 2: /* address port 1 */\r
1297       ym2612.OPN.ST.address = d;\r
1298       ym2612.addr_A1 = (a & 2) >> 1;\r
1299 #ifdef __GP2X__\r
1300       if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, -1);\r
1301 #endif\r
1302       return 0;\r
1303 \r
1304     case 1: /* data port 0    */\r
1305     case 3: /* data port 1    */\r
1306       addr = ym2612.OPN.ST.address | ((int)ym2612.addr_A1 << 8);\r
1307       ym2612.REGS[addr] = d;\r
1308 \r
1309       // the busy flag in the YM2612 status is actually a 32 cycle timer\r
1310       // (89.6 Z80 cycles), triggered by any write to the data port.\r
1311       Pico.t.ym2612_busy = (cycles << 8) + YMBUSY_ZCYCLES; // Q8 for convenience\r
1312 \r
1313       switch (addr)\r
1314       {\r
1315         case 0x24: // timer A High 8\r
1316         case 0x25: { // timer A Low 2\r
1317           int TAnew = (addr == 0x24) ? ((ym2612.OPN.ST.TA & 0x03)|(((int)d)<<2))\r
1318                                      : ((ym2612.OPN.ST.TA & 0x3fc)|(d&3));\r
1319           if (ym2612.OPN.ST.TA != TAnew)\r
1320           {\r
1321             ym2612_sync_timers(cycles, ym2612.OPN.ST.mode, ym2612.OPN.ST.mode);\r
1322             //elprintf(EL_STATUS, "timer a set %i", TAnew);\r
1323             ym2612.OPN.ST.TA = TAnew;\r
1324             //ym2612.OPN.ST.TAC = (1024-TAnew)*18;\r
1325             //ym2612.OPN.ST.TAT = 0;\r
1326             Pico.t.timer_a_step = TIMER_A_TICK_ZCYCLES * (1024 - TAnew);\r
1327             elprintf(EL_YMTIMER, "timer a set to %i, %i", 1024 - TAnew, Pico.t.timer_a_next_oflow>>8);\r
1328           }\r
1329           return 0;\r
1330         }\r
1331         case 0x26: // timer B\r
1332           if (ym2612.OPN.ST.TB != d) {\r
1333             ym2612_sync_timers(cycles, ym2612.OPN.ST.mode, ym2612.OPN.ST.mode);\r
1334             //elprintf(EL_STATUS, "timer b set %i", d);\r
1335             ym2612.OPN.ST.TB = d;\r
1336             //ym2612.OPN.ST.TBC = (256-d) * 288;\r
1337             //ym2612.OPN.ST.TBT  = 0;\r
1338             Pico.t.timer_b_step = TIMER_B_TICK_ZCYCLES * (256 - d);\r
1339             elprintf(EL_YMTIMER, "timer b set to %i, %i", 256 - d, Pico.t.timer_b_next_oflow>>8);\r
1340           }\r
1341           return 0;\r
1342         case 0x27: { /* mode, timer control */\r
1343           int old_mode = ym2612.OPN.ST.mode;\r
1344 \r
1345           elprintf(EL_YMTIMER, "st mode %02x", d);\r
1346           ym2612_sync_timers(cycles, old_mode, d);\r
1347 \r
1348           ym2612.OPN.ST.mode = d;\r
1349 \r
1350           /* reset Timer a flag */\r
1351           if (d & 0x10)\r
1352             ym2612.OPN.ST.status &= ~1;\r
1353 \r
1354           /* reset Timer b flag */\r
1355           if (d & 0x20)\r
1356             ym2612.OPN.ST.status &= ~2;\r
1357 \r
1358           if ((d ^ old_mode) & 0xc0) {\r
1359 #ifdef __GP2X__\r
1360             if (PicoIn.opt & POPT_EXT_FM) return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
1361 #endif\r
1362             PsndDoFM(cycles);\r
1363             return 1;\r
1364           }\r
1365           return 0;\r
1366         }\r
1367         case 0x2a: { /* DAC data */\r
1368           //elprintf(EL_STATUS, "%03i dac w %08x z80 %i", cycles, d, is_from_z80);\r
1369           if (ym2612.dacen)\r
1370             PsndDoDAC(cycles);\r
1371           ym2612.dacout = ((int)d - 0x80) << DAC_SHIFT;\r
1372           return 0;\r
1373         }\r
1374         case 0x2b: { /* DAC Sel  (YM2612) */\r
1375           ym2612.dacen = d & 0x80;\r
1376 #ifdef __GP2X__\r
1377           if (PicoIn.opt & POPT_EXT_FM) YM2612Write_940(a, d, get_scanline(is_from_z80));\r
1378 #endif\r
1379           return 0;\r
1380         }\r
1381       }\r
1382       break;\r
1383   }\r
1384 \r
1385 #ifdef __GP2X__\r
1386   if (PicoIn.opt & POPT_EXT_FM)\r
1387     return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
1388 #endif\r
1389   PsndDoFM(cycles);\r
1390   return YM2612Write_(a, d);\r
1391 }\r
1392 \r
1393 \r
1394 static u32 ym2612_read_local_z80(void)\r
1395 {\r
1396   int xcycles = z80_cyclesDone() << 8;\r
1397 \r
1398   ym2612_update_status(xcycles);\r
1399 \r
1400   elprintf(EL_YMTIMER, "timer z80 read %i, sched %i, %i @ %i|%i",\r
1401     ym2612.OPN.ST.status, Pico.t.timer_a_next_oflow >> 8,\r
1402     Pico.t.timer_b_next_oflow >> 8, xcycles >> 8, (xcycles >> 8) / 228);\r
1403   return ym2612.OPN.ST.status;\r
1404 }\r
1405 \r
1406 static u32 ym2612_read_local_68k(void)\r
1407 {\r
1408   int xcycles = z80_cycles_from_68k() << 8;\r
1409 \r
1410   ym2612_update_status(xcycles);\r
1411 \r
1412   elprintf(EL_YMTIMER, "timer 68k read %i, sched %i, %i @ %i|%i",\r
1413     ym2612.OPN.ST.status, Pico.t.timer_a_next_oflow >> 8,\r
1414     Pico.t.timer_b_next_oflow >> 8, xcycles >> 8, (xcycles >> 8) / 228);\r
1415   return ym2612.OPN.ST.status;\r
1416 }\r
1417 \r
1418 // legacy code, only used for GP2X\r
1419 void ym2612_pack_state_old(void)\r
1420 {\r
1421   // timers are saved as tick counts, in 16.16 int format\r
1422   int tat = 0, tbt = 0, busy = 0;\r
1423   u8 buf[16];\r
1424   size_t b = 0;\r
1425 \r
1426   ym2612_pack_timers(buf, sizeof(buf));\r
1427   load_u16(buf, &b), load_u16(buf, &b);\r
1428   tat = load_u32(buf, &b);\r
1429   tbt = load_u32(buf, &b);\r
1430   busy = load_u32(buf, &b);\r
1431 \r
1432 #ifdef __GP2X__\r
1433   if (PicoIn.opt & POPT_EXT_FM)\r
1434     YM2612PicoStateSave2_940(tat, tbt);\r
1435   else\r
1436 #endif\r
1437     YM2612PicoStateSave2(tat, tbt, busy);\r
1438 }\r
1439 \r
1440 int ym2612_pack_timers(void *buf, size_t size)\r
1441 {\r
1442   // timers are saved as tick counts, in 16.16 int format\r
1443   int tac, tat = 0, tbc, tbt = 0, busy = 0;\r
1444   size_t b = 0;\r
1445 \r
1446   tac = 1024 - ym2612.OPN.ST.TA;\r
1447   tbc = 256  - ym2612.OPN.ST.TB;\r
1448   if (Pico.t.ym2612_busy > 0)\r
1449     busy = cycles_z80_to_68k(Pico.t.ym2612_busy);\r
1450   if (Pico.t.timer_a_next_oflow != TIMER_NO_OFLOW)\r
1451     tat = ((Pico.t.timer_a_step - Pico.t.timer_a_next_oflow) * ((1LL<<32)/TIMER_A_TICK_ZCYCLES+1))>>16;\r
1452   if (Pico.t.timer_b_next_oflow != TIMER_NO_OFLOW)\r
1453     tbt = ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow) * ((1LL<<32)/TIMER_B_TICK_ZCYCLES+1))>>16;\r
1454   elprintf(EL_YMTIMER, "save: timer a %i/%i", tat >> 16, tac);\r
1455   elprintf(EL_YMTIMER, "save: timer b %i/%i", tbt >> 16, tbc);\r
1456 \r
1457   assert(size >= 16);\r
1458   save_u16(buf, &b, ym2612.OPN.ST.TA);\r
1459   save_u16(buf, &b, ym2612.OPN.ST.TB);\r
1460   save_u32(buf, &b, tat);\r
1461   save_u32(buf, &b, tbt);\r
1462   save_u32(buf, &b, busy);\r
1463   return b;\r
1464 }\r
1465 \r
1466 // legacy code, only used for GP2X\r
1467 void ym2612_unpack_state_old(void)\r
1468 {\r
1469   int i, ret, tat, tbt, busy = 0;\r
1470   YM2612PicoStateLoad();\r
1471   Pico.t.m68c_frame_start = SekCyclesDone();\r
1472 \r
1473   // feed all the registers and update internal state\r
1474   for (i = 0x20; i < 0xA0; i++) {\r
1475     ym2612_write_local(0, i, 0);\r
1476     ym2612_write_local(1, ym2612.REGS[i], 0);\r
1477   }\r
1478   for (i = 0x30; i < 0xA0; i++) {\r
1479     ym2612_write_local(2, i, 0);\r
1480     ym2612_write_local(3, ym2612.REGS[i|0x100], 0);\r
1481   }\r
1482   for (i = 0xAF; i >= 0xA0; i--) { // must apply backwards\r
1483     ym2612_write_local(2, i, 0);\r
1484     ym2612_write_local(3, ym2612.REGS[i|0x100], 0);\r
1485     ym2612_write_local(0, i, 0);\r
1486     ym2612_write_local(1, ym2612.REGS[i], 0);\r
1487   }\r
1488   for (i = 0xB0; i < 0xB8; i++) {\r
1489     ym2612_write_local(0, i, 0);\r
1490     ym2612_write_local(1, ym2612.REGS[i], 0);\r
1491     ym2612_write_local(2, i, 0);\r
1492     ym2612_write_local(3, ym2612.REGS[i|0x100], 0);\r
1493   }\r
1494 \r
1495 #ifdef __GP2X__\r
1496   if (PicoIn.opt & POPT_EXT_FM)\r
1497     ret = YM2612PicoStateLoad2_940(&tat, &tbt);\r
1498   else\r
1499 #endif\r
1500     ret = YM2612PicoStateLoad2(&tat, &tbt, &busy);\r
1501   if (ret != 0) {\r
1502     elprintf(EL_STATUS, "old ym2612 state");\r
1503     return; // no saved timers\r
1504   }\r
1505   {\r
1506     u8 tmp[16];\r
1507     size_t b = 0;\r
1508     save_u16(tmp, &b, ym2612.OPN.ST.TA);\r
1509     save_u16(tmp, &b, ym2612.OPN.ST.TB);\r
1510     save_u32(tmp, &b, tat);\r
1511     save_u32(tmp, &b, tbt);\r
1512     save_u32(tmp, &b, busy);\r
1513     ym2612_unpack_timers(tmp, b);\r
1514   }\r
1515 }\r
1516 \r
1517 void ym2612_unpack_timers(const void *buf, size_t size)\r
1518 {\r
1519   int tac, tat, tbc, tbt, busy;\r
1520   size_t b = 0;\r
1521   assert(size >= 16);\r
1522   if (size < 16)\r
1523     return;\r
1524   ym2612.OPN.ST.TA = load_u16(buf, &b);\r
1525   ym2612.OPN.ST.TB = load_u16(buf, &b);\r
1526   tat = load_u32(buf, &b);\r
1527   tbt = load_u32(buf, &b);\r
1528   busy = load_u32(buf, &b);\r
1529   Pico.t.ym2612_busy = cycles_68k_to_z80(busy);\r
1530   tac = (1024 - ym2612.OPN.ST.TA) << 16;\r
1531   tbc = (256  - ym2612.OPN.ST.TB) << 16;\r
1532   if (ym2612.OPN.ST.mode & 1)\r
1533     Pico.t.timer_a_next_oflow = (1LL * (tac-tat) * TIMER_A_TICK_ZCYCLES)>>16;\r
1534   else\r
1535     Pico.t.timer_a_next_oflow = TIMER_NO_OFLOW;\r
1536   if (ym2612.OPN.ST.mode & 2)\r
1537     Pico.t.timer_b_next_oflow = (1LL * (tbc-tbt) * TIMER_B_TICK_ZCYCLES)>>16;\r
1538   else\r
1539     Pico.t.timer_b_next_oflow = TIMER_NO_OFLOW;\r
1540   elprintf(EL_YMTIMER, "load: %i/%i, timer_a_next_oflow %i", tat>>16, tac>>16, Pico.t.timer_a_next_oflow >> 8);\r
1541   elprintf(EL_YMTIMER, "load: %i/%i, timer_b_next_oflow %i", tbt>>16, tbc>>16, Pico.t.timer_b_next_oflow >> 8);\r
1542 }\r
1543 \r
1544 #if defined(NO_32X) && defined(_ASM_MEMORY_C)\r
1545 // referenced by asm code\r
1546 u32 PicoRead8_32x(u32 a) { return 0; }\r
1547 u32 PicoRead16_32x(u32 a) { return 0; }\r
1548 void PicoWrite8_32x(u32 a, u32 d) {}\r
1549 void PicoWrite16_32x(u32 a, u32 d) {}\r
1550 #endif\r
1551 \r
1552 // -----------------------------------------------------------------\r
1553 //                        z80 memhandlers\r
1554 \r
1555 static void access_68k_bus(int delay) // bus delay as Q8\r
1556 {\r
1557   // TODO: if the 68K is in DMA wait, Z80 has to wait until DMA ends\r
1558 \r
1559   // 68k bus access delay for z80. The fractional part needs to be accumulated\r
1560   // until an additional cycle is full. That is then added to the integer part.\r
1561   Pico.t.z80_busdelay += (delay&0xff); // accumulate\r
1562   z80_subCLeft((delay>>8) + (Pico.t.z80_busdelay>>8));\r
1563   Pico.t.z80_busdelay &= 0xff; // leftover cycle fraction\r
1564   // don't use SekCyclesBurn() here since the Z80 doesn't run in cycle lock to\r
1565   // the 68K. Count the stolen cycles to be accounted later in the 68k CPU runs\r
1566   Pico.t.z80_buscycles += 0x80; // TODO <=8.4 for Rick 2, but >=8.9 for misc_test\r
1567 }\r
1568 \r
1569 static unsigned char z80_md_vdp_read(unsigned short a)\r
1570 {\r
1571   if ((a & 0xff00) == 0x7f00) {\r
1572     // 68k bus access delay=3.3 per kabuto, for notaz picotest 2.42<delay<2.57?\r
1573     access_68k_bus(0x280); // Q8, picotest: 0x26d(>2.42) - 0x292(<2.57)\r
1574 \r
1575     switch (a & 0x0d)\r
1576     {\r
1577       case 0x00: return PicoVideoRead8DataH(1);\r
1578       case 0x01: return PicoVideoRead8DataL(1);\r
1579       case 0x04: return PicoVideoRead8CtlH(1);\r
1580       case 0x05: return PicoVideoRead8CtlL(1);\r
1581       case 0x08:\r
1582       case 0x0c: return PicoVideoGetV(get_scanline(1), 1);\r
1583       case 0x09:\r
1584       case 0x0d: return Pico.m.rotate++;\r
1585     }\r
1586   }\r
1587 \r
1588   elprintf(EL_ANOMALY, "z80 invalid r8 [%06x] %02x", a, 0xff);\r
1589   return 0xff;\r
1590 }\r
1591 \r
1592 static unsigned char z80_md_bank_read(unsigned short a)\r
1593 {\r
1594   unsigned int addr68k;\r
1595   unsigned char ret = 0xff;\r
1596 \r
1597   // 68k bus access delay=3.3 per kabuto, but for notaz picotest 3.02<delay<3.32\r
1598   access_68k_bus(0x340); // Q8, picotest: 0x306(>3.02)-0x351(<3.32)\r
1599 \r
1600   addr68k = Pico.m.z80_bank68k << 15;\r
1601   addr68k |= a & 0x7fff;\r
1602 \r
1603   if (addr68k < 0xe00000) // can't read from 68K RAM\r
1604     ret = m68k_read8(addr68k);\r
1605 \r
1606   elprintf(EL_Z80BNK, "z80->68k r8 [%06x] %02x", addr68k, ret);\r
1607   return ret;\r
1608 }\r
1609 \r
1610 static void z80_md_ym2612_write(unsigned int a, unsigned char data)\r
1611 {\r
1612   if (PicoIn.opt & POPT_EN_FM)\r
1613     ym2612_write_local(a, data, 1);\r
1614 }\r
1615 \r
1616 static void z80_md_vdp_br_write(unsigned int a, unsigned char data)\r
1617 {\r
1618   if ((a&0xfff9) == 0x7f11) // 7f11 7f13 7f15 7f17\r
1619   {\r
1620     psg_write_z80(data);\r
1621     return;\r
1622   }\r
1623   // at least VDP data writes hang my machine\r
1624 \r
1625   if ((a>>8) == 0x60)\r
1626   {\r
1627     Pico.m.z80_bank68k >>= 1;\r
1628     Pico.m.z80_bank68k |= data << 8;\r
1629     Pico.m.z80_bank68k &= 0x1ff; // 9 bits and filled in the new top one\r
1630     return;\r
1631   }\r
1632 \r
1633   elprintf(EL_ANOMALY, "z80 invalid w8 [%06x] %02x", a, data);\r
1634 }\r
1635 \r
1636 static void z80_md_bank_write(unsigned int a, unsigned char data)\r
1637 {\r
1638   unsigned int addr68k;\r
1639 \r
1640   // 68k bus access delay=3.3 per kabuto, but for notaz picotest 3.02<delay<3.32\r
1641   access_68k_bus(0x340); // Q8, picotest: 0x306(>3.02)-0x351(<3.32)\r
1642 \r
1643   addr68k = Pico.m.z80_bank68k << 15;\r
1644   addr68k += a & 0x7fff;\r
1645 \r
1646   elprintf(EL_Z80BNK, "z80->68k w8 [%06x] %02x", addr68k, data);\r
1647   m68k_write8(addr68k, data);\r
1648 }\r
1649 \r
1650 // -----------------------------------------------------------------\r
1651 \r
1652 static unsigned char z80_md_in(unsigned short p)\r
1653 {\r
1654   elprintf(EL_ANOMALY, "Z80 port %04x read", p);\r
1655   return 0xff;\r
1656 }\r
1657 \r
1658 static void z80_md_out(unsigned short p, unsigned char d)\r
1659 {\r
1660   elprintf(EL_ANOMALY, "Z80 port %04x write %02x", p, d);\r
1661 }\r
1662 \r
1663 static void z80_mem_setup(void)\r
1664 {\r
1665   z80_map_set(z80_read_map, 0x0000, 0x1fff, PicoMem.zram, 0);\r
1666   z80_map_set(z80_read_map, 0x2000, 0x3fff, PicoMem.zram, 0);\r
1667   z80_map_set(z80_read_map, 0x4000, 0x5fff, ym2612_read_local_z80, 1);\r
1668   z80_map_set(z80_read_map, 0x6000, 0x7fff, z80_md_vdp_read, 1);\r
1669   z80_map_set(z80_read_map, 0x8000, 0xffff, z80_md_bank_read, 1);\r
1670 \r
1671   z80_map_set(z80_write_map, 0x0000, 0x1fff, PicoMem.zram, 0);\r
1672   z80_map_set(z80_write_map, 0x2000, 0x3fff, PicoMem.zram, 0);\r
1673   z80_map_set(z80_write_map, 0x4000, 0x5fff, z80_md_ym2612_write, 1);\r
1674   z80_map_set(z80_write_map, 0x6000, 0x7fff, z80_md_vdp_br_write, 1);\r
1675   z80_map_set(z80_write_map, 0x8000, 0xffff, z80_md_bank_write, 1);\r
1676 \r
1677 #ifdef _USE_DRZ80\r
1678   drZ80.z80_in = z80_md_in;\r
1679   drZ80.z80_out = z80_md_out;\r
1680 #endif\r
1681 #ifdef _USE_CZ80\r
1682   Cz80_Set_INPort(&CZ80, z80_md_in);\r
1683   Cz80_Set_OUTPort(&CZ80, z80_md_out);\r
1684 #endif\r
1685 }\r
1686 \r
1687 // vim:shiftwidth=2:ts=2:expandtab\r