6d061c2adbcc541e4f91b4ccafb398d85b933cbb
[picodrive.git] / pico / pico.c
1 /*\r
2  * PicoDrive\r
3  * (c) Copyright Dave, 2004\r
4  * (C) notaz, 2006-2010\r
5  * (C) irixxxx, 2020-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 "pico_int.h"\r
12 #include "sound/ym2612.h"\r
13 \r
14 struct Pico Pico;\r
15 struct PicoMem PicoMem;\r
16 PicoInterface PicoIn;\r
17 \r
18 void (*PicoResetHook)(void) = NULL;\r
19 void (*PicoLineHook)(void) = NULL;\r
20 \r
21 // to be called once on emu init\r
22 void PicoInit(void)\r
23 {\r
24   // Blank space for state:\r
25   memset(&Pico,0,sizeof(Pico));\r
26   memset(&PicoMem,0,sizeof(PicoMem));\r
27   memset(&PicoIn.pad,0,sizeof(PicoIn.pad));\r
28   memset(&PicoIn.padInt,0,sizeof(PicoIn.padInt));\r
29 \r
30   Pico.est.Pico = &Pico;\r
31   Pico.est.PicoMem_vram = PicoMem.vram;\r
32   Pico.est.PicoMem_cram = PicoMem.cram;\r
33   Pico.est.PicoOpt = &PicoIn.opt;\r
34 \r
35   // Init CPUs:\r
36   SekInit();\r
37   z80_init(); // init even if we aren't going to use it\r
38 \r
39   PicoInitMCD();\r
40   PicoSVPInit();\r
41   Pico32xInit();\r
42   PsndInit();\r
43 \r
44   PicoVideoInit();\r
45   PicoDrawInit();\r
46   PicoDraw2Init();\r
47 }\r
48 \r
49 // to be called once on emu exit\r
50 void PicoExit(void)\r
51 {\r
52   if (PicoIn.AHW & PAHW_MCD)\r
53     PicoExitMCD();\r
54   PicoCartUnload();\r
55   z80_exit();\r
56   PsndExit();\r
57 \r
58   free(Pico.sv.data);\r
59   Pico.sv.data = NULL;\r
60   Pico.sv.start = Pico.sv.end = 0;\r
61   pevt_dump();\r
62 }\r
63 \r
64 void PicoPower(void)\r
65 {\r
66   Pico.m.frame_count = 0;\r
67   Pico.t.m68c_cnt = Pico.t.m68c_aim = 0;\r
68 \r
69   // clear all memory of the emulated machine\r
70   memset(&PicoMem,0,sizeof(PicoMem));\r
71 \r
72   memset(&Pico.video,0,sizeof(Pico.video));\r
73   memset(&Pico.m,0,sizeof(Pico.m));\r
74   memset(&Pico.t,0,sizeof(Pico.t));\r
75 \r
76   // my MD1 VA6 console has this in IO\r
77   PicoMem.ioports[1] = PicoMem.ioports[2] = PicoMem.ioports[3] = 0xff;\r
78 \r
79   Pico.video.hint_irq = (PicoIn.AHW & PAHW_PICO ? 5 : 4);\r
80 \r
81   if (PicoIn.AHW & PAHW_MCD)\r
82     PicoPowerMCD();\r
83 \r
84   if (PicoIn.opt & POPT_EN_32X)\r
85     PicoPower32x();\r
86 \r
87   PicoReset();\r
88 \r
89   // powerup default VDP register values from TMSS BIOS\r
90   Pico.video.reg[0] = Pico.video.reg[1] = 0x04;\r
91   Pico.video.reg[0xc] = 0x81;\r
92   Pico.video.reg[0xf] = 0x02;\r
93   SATaddr = 0x0000;\r
94   SATmask = ~0x3ff;\r
95 }\r
96 \r
97 PICO_INTERNAL void PicoDetectRegion(void)\r
98 {\r
99   int support=0, hw=0, i;\r
100   unsigned char pal=0;\r
101   char *pr = (char *)(Pico.rom + 0x1f0);\r
102 \r
103   if (PicoIn.regionOverride)\r
104   {\r
105     support = PicoIn.regionOverride;\r
106   }\r
107   else if (strcmp(pr, "EUROPE") == 0 || strcmp(pr, "Europe") == 0)\r
108   {\r
109     // Unusual cartridge region 'code'\r
110     support|=8;\r
111   }\r
112   else\r
113   {\r
114     // Read cartridge region data:\r
115     unsigned short *rd = (unsigned short *)pr;\r
116     int region = (rd[0] << 16) | rd[1];\r
117 \r
118     for (i = 0; i < 4; i++)\r
119     {\r
120       int c;\r
121 \r
122       c = region >> (i<<3);\r
123       c &= 0xff;\r
124       if (c <= ' ') continue;\r
125 \r
126            if (c=='J')  support|=1;\r
127       else if (c=='U')  support|=4;\r
128       else if (c=='E')  support|=8;\r
129       else if (c=='j') {support|=1; break; }\r
130       else if (c=='u') {support|=4; break; }\r
131       else if (c=='e') {support|=8; break; }\r
132       else\r
133       {\r
134         // New style code:\r
135         char s[2]={0,0};\r
136         s[0]=(char)c;\r
137         support|=strtol(s,NULL,16);\r
138       }\r
139     }\r
140   }\r
141 \r
142   // auto detection order override\r
143   if (PicoIn.autoRgnOrder) {\r
144          if (((PicoIn.autoRgnOrder>>0)&0xf) & support) support = (PicoIn.autoRgnOrder>>0)&0xf;\r
145     else if (((PicoIn.autoRgnOrder>>4)&0xf) & support) support = (PicoIn.autoRgnOrder>>4)&0xf;\r
146     else if (((PicoIn.autoRgnOrder>>8)&0xf) & support) support = (PicoIn.autoRgnOrder>>8)&0xf;\r
147   }\r
148 \r
149   // Try to pick the best hardware value for English/50hz:\r
150        if (support&8) { hw=0xc0; pal=1; } // Europe\r
151   else if (support&4)   hw=0x80;          // USA\r
152   else if (support&2) { hw=0x40; pal=1; } // Japan PAL\r
153   else if (support&1)   hw=0x00;          // Japan NTSC\r
154   else hw=0x80; // USA\r
155 \r
156   if (!(PicoIn.AHW & PAHW_MCD)) hw |= 0x20; // No disk attached\r
157 \r
158   Pico.m.hardware=(unsigned char)hw; \r
159   Pico.m.pal=pal;\r
160 }\r
161 \r
162 int PicoReset(void)\r
163 {\r
164   if (Pico.romsize <= 0)\r
165     return 1;\r
166 \r
167 #if defined(CPU_CMP_R) || defined(CPU_CMP_W) || defined(DRC_CMP)\r
168   PicoIn.opt |= POPT_DIS_VDP_FIFO|POPT_DIS_IDLE_DET;\r
169 #endif\r
170 \r
171   /* must call now, so that banking is reset, and correct vectors get fetched */\r
172   if (PicoResetHook)\r
173     PicoResetHook();\r
174 \r
175   memset(&PicoIn.padInt, 0, sizeof(PicoIn.padInt));\r
176 \r
177   z80_reset();\r
178   if (PicoIn.AHW & PAHW_SMS) {\r
179     PicoResetMS();\r
180     return 0;\r
181   }\r
182 \r
183   SekReset();\r
184   // ..but do not reset SekCycle* to not desync with addons\r
185 \r
186   // s68k doesn't have the TAS quirk, so we just globally set normal TAS handler in MCD mode (used by Batman games).\r
187   SekSetRealTAS(PicoIn.AHW & PAHW_MCD);\r
188 \r
189   Pico.m.z80_bank68k = 0;\r
190   Pico.m.z80_reset = 1;\r
191 \r
192   PicoDetectRegion();\r
193 \r
194   PicoVideoReset();\r
195 \r
196   PsndReset(); // pal must be known here\r
197 \r
198   // create an empty "dma" to cause 68k exec start at random frame location\r
199   Pico.t.m68c_line_start = Pico.t.m68c_aim;\r
200   PicoVideoFIFOWrite(rand() & 0x1fff, 0, 0, PVS_CPURD);\r
201 \r
202   SekFinishIdleDet();\r
203 \r
204   if (PicoIn.opt & POPT_EN_32X)\r
205     PicoReset32x();\r
206 \r
207   if (PicoIn.AHW & PAHW_MCD) {\r
208     PicoResetMCD();\r
209     return 0;\r
210   }\r
211 \r
212   // reinit, so that checksum checks pass\r
213   if (!(PicoIn.opt & POPT_DIS_IDLE_DET))\r
214     SekInitIdleDet();\r
215 \r
216   // reset sram state; enable sram access by default if it doesn't overlap with ROM\r
217   Pico.m.sram_reg = 0;\r
218   if ((Pico.sv.flags & SRF_EEPROM) || Pico.romsize <= Pico.sv.start)\r
219     Pico.m.sram_reg |= SRR_MAPPED;\r
220 \r
221   if (Pico.sv.flags & SRF_ENABLED)\r
222     elprintf(EL_STATUS, "sram: %06x - %06x; eeprom: %i", Pico.sv.start, Pico.sv.end,\r
223       !!(Pico.sv.flags & SRF_EEPROM));\r
224 \r
225   return 0;\r
226 }\r
227 \r
228 // flush config changes before emu loop starts\r
229 void PicoLoopPrepare(void)\r
230 {\r
231   if (PicoIn.regionOverride)\r
232     // force setting possibly changed..\r
233     Pico.m.pal = (PicoIn.regionOverride == 2 || PicoIn.regionOverride == 8) ? 1 : 0;\r
234 \r
235   if (Pico.m.pal) {\r
236     Pico.t.vcnt_wrap = 0x103;\r
237     Pico.t.vcnt_adj = 57;\r
238   }\r
239   else {\r
240     Pico.t.vcnt_wrap = 0xEB;\r
241     Pico.t.vcnt_adj = 6;\r
242   }\r
243   PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);\r
244 \r
245   Pico.m.dirtyPal = 1;\r
246   rendstatus_old = -1;\r
247 \r
248   if (PicoIn.AHW & PAHW_MCD)\r
249     PicoMCDPrepare();\r
250   if (PicoIn.AHW & PAHW_32X)\r
251     Pico32xPrepare();\r
252 }\r
253 \r
254 #include "pico_cmn.c"\r
255 \r
256 /* sync z80 to 68k */\r
257 PICO_INTERNAL void PicoSyncZ80(unsigned int m68k_cycles_done)\r
258 {\r
259   int m68k_cnt;\r
260   int cnt;\r
261 \r
262   m68k_cnt = m68k_cycles_done - Pico.t.m68c_frame_start;\r
263   Pico.t.z80c_aim = cycles_68k_to_z80(m68k_cnt);\r
264   cnt = Pico.t.z80c_aim - Pico.t.z80c_cnt;\r
265 \r
266   pprof_start(z80);\r
267 \r
268   elprintf(EL_BUSREQ, "z80 sync %i (%u|%u -> %u|%u)", cnt,\r
269     Pico.t.z80c_cnt, Pico.t.z80c_cnt * 15 / 7 / 488,\r
270     Pico.t.z80c_aim, Pico.t.z80c_aim * 15 / 7 / 488);\r
271 \r
272   if (cnt > 0)\r
273     Pico.t.z80c_cnt += z80_run(cnt);\r
274 \r
275   pprof_end(z80);\r
276 }\r
277 \r
278 \r
279 void PicoFrame(void)\r
280 {\r
281   pprof_start(frame);\r
282 \r
283   Pico.m.frame_count++;\r
284 \r
285   if (PicoIn.AHW & PAHW_SMS) {\r
286     PicoFrameMS();\r
287     goto end;\r
288   }\r
289 \r
290   if (PicoIn.AHW & PAHW_32X) {\r
291     PicoFrame32x(); // also does MCD+32X\r
292     goto end;\r
293   }\r
294 \r
295   if (PicoIn.AHW & PAHW_MCD) {\r
296     PicoFrameMCD();\r
297     goto end;\r
298   }\r
299 \r
300   //if(Pico.video.reg[12]&0x2) Pico.video.status ^= SR_ODD; // change odd bit in interlace mode\r
301 \r
302   PicoFrameStart();\r
303   PicoFrameHints();\r
304 \r
305 end:\r
306   pprof_end(frame);\r
307 }\r
308 \r
309 void PicoFrameDrawOnly(void)\r
310 {\r
311   if (!(PicoIn.AHW & PAHW_SMS)) {\r
312     PicoFrameStart();\r
313     PicoDrawSync(Pico.m.pal?239:223, 0, 0);\r
314   } else {\r
315     PicoFrameDrawOnlyMS();\r
316   }\r
317 }\r
318 \r
319 void PicoGetInternal(pint_t which, pint_ret_t *r)\r
320 {\r
321   switch (which)\r
322   {\r
323     case PI_ROM:         r->vptr = Pico.rom; break;\r
324     case PI_ISPAL:       r->vint = Pico.m.pal; break;\r
325     case PI_IS40_CELL:   r->vint = Pico.video.reg[12]&1; break;\r
326     case PI_IS240_LINES: r->vint = Pico.m.pal && (Pico.video.reg[1]&8); break;\r
327   }\r
328 }\r
329 \r
330 // vim:ts=2:sw=2:expandtab\r