e305e3b17882ed784faf7f5e5a3d7f2cab464171
[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   PicoCartUnload();\r
53   if (PicoIn.AHW & PAHW_MCD)\r
54     PicoExitMCD();\r
55   z80_exit();\r
56   PsndExit();\r
57   PicoCloseTape();\r
58 \r
59   free(Pico.sv.data);\r
60   Pico.sv.data = NULL;\r
61   Pico.sv.start = Pico.sv.end = 0;\r
62   pevt_dump();\r
63 }\r
64 \r
65 void PicoPower(void)\r
66 {\r
67   Pico.m.frame_count = 0;\r
68   Pico.t.m68c_cnt = Pico.t.m68c_aim = 0;\r
69 \r
70   // clear all memory of the emulated machine\r
71   memset(&PicoMem,0,sizeof(PicoMem));\r
72 \r
73   memset(&Pico.video,0,sizeof(Pico.video));\r
74   memset(&Pico.m,0,sizeof(Pico.m));\r
75   memset(&Pico.t,0,sizeof(Pico.t));\r
76 \r
77   // my MD1 VA6 console has this in IO\r
78   PicoMem.ioports[1] = PicoMem.ioports[2] = PicoMem.ioports[3] = 0xff;\r
79 \r
80   Pico.video.hint_irq = (PicoIn.AHW & PAHW_PICO ? 5 : 4);\r
81 \r
82   if (PicoIn.AHW & PAHW_MCD)\r
83     PicoPowerMCD();\r
84 \r
85   if (PicoIn.opt & POPT_EN_32X)\r
86     PicoPower32x();\r
87 \r
88   PicoReset();\r
89 \r
90   // powerup default VDP register values from TMSS BIOS\r
91   Pico.video.reg[0] = Pico.video.reg[1] = 0x04;\r
92   Pico.video.reg[0xc] = 0x81;\r
93   Pico.video.reg[0xf] = 0x02;\r
94   SATaddr = 0x0000;\r
95   SATmask = ~0x3ff;\r
96 }\r
97 \r
98 PICO_INTERNAL void PicoDetectRegion(void)\r
99 {\r
100   int support=0, hw=0, i;\r
101   unsigned char pal=0;\r
102   char *pr = (char *)(Pico.rom + 0x1f0);\r
103 \r
104   if (PicoIn.regionOverride)\r
105   {\r
106     support = PicoIn.regionOverride;\r
107   }\r
108   else if (strcmp(pr, "EUROPE") == 0 || strcmp(pr, "Europe") == 0)\r
109   {\r
110     // Unusual cartridge region 'code'\r
111     support|=8;\r
112   }\r
113   else\r
114   {\r
115     // Read cartridge region data:\r
116     unsigned short *rd = (unsigned short *)pr;\r
117     int region = (rd[0] << 16) | rd[1];\r
118 \r
119     for (i = 0; i < 4; i++)\r
120     {\r
121       int c;\r
122 \r
123       c = region >> (i<<3);\r
124       c &= 0xff;\r
125       if (c <= ' ') continue;\r
126 \r
127            if (c=='J')  support|=1;\r
128       else if (c=='U')  support|=4;\r
129       else if (c=='E')  support|=8;\r
130       else if (c=='j') {support|=1; break; }\r
131       else if (c=='u') {support|=4; break; }\r
132       else if (c=='e') {support|=8; break; }\r
133       else\r
134       {\r
135         // New style code:\r
136         char s[2]={0,0};\r
137         s[0]=(char)c;\r
138         support|=strtol(s,NULL,16);\r
139       }\r
140     }\r
141   }\r
142 \r
143   // auto detection order override\r
144   if (PicoIn.autoRgnOrder) {\r
145          if (((PicoIn.autoRgnOrder>>0)&0xf) & support) support = (PicoIn.autoRgnOrder>>0)&0xf;\r
146     else if (((PicoIn.autoRgnOrder>>4)&0xf) & support) support = (PicoIn.autoRgnOrder>>4)&0xf;\r
147     else if (((PicoIn.autoRgnOrder>>8)&0xf) & support) support = (PicoIn.autoRgnOrder>>8)&0xf;\r
148   }\r
149 \r
150   // Try to pick the best hardware value for English/50hz:\r
151        if (support&8) { hw=0xc0; pal=1; } // Europe\r
152   else if (support&4)   hw=0x80;          // USA\r
153   else if (support&2) { hw=0x40; pal=1; } // Japan PAL\r
154   else if (support&1)   hw=0x00;          // Japan NTSC\r
155   else hw=0x80; // USA\r
156 \r
157   if (!(PicoIn.AHW & PAHW_MCD)) hw |= 0x20; // No disk attached\r
158 \r
159   Pico.m.hardware=(unsigned char)hw; \r
160   Pico.m.pal=pal;\r
161 }\r
162 \r
163 int PicoReset(void)\r
164 {\r
165 #if defined(CPU_CMP_R) || defined(CPU_CMP_W) || defined(DRC_CMP)\r
166   PicoIn.opt |= POPT_DIS_VDP_FIFO|POPT_DIS_IDLE_DET;\r
167 #endif\r
168 \r
169   /* must call now, so that banking is reset, and correct vectors get fetched */\r
170   if (PicoResetHook)\r
171     PicoResetHook();\r
172 \r
173   memset(&PicoIn.padInt, 0, sizeof(PicoIn.padInt));\r
174 \r
175   z80_reset();\r
176   if (PicoIn.AHW & PAHW_SMS) {\r
177     PicoResetMS();\r
178     return 0;\r
179   }\r
180 \r
181   SekReset();\r
182   // ..but do not reset SekCycle* to not desync with addons\r
183 \r
184   // s68k doesn't have the TAS quirk, so we just globally set normal TAS handler in MCD mode (used by Batman games).\r
185   SekSetRealTAS(PicoIn.AHW & PAHW_MCD);\r
186 \r
187   Pico.m.z80_bank68k = 0;\r
188   Pico.m.z80_reset = 1;\r
189 \r
190   PicoDetectRegion();\r
191 \r
192   PicoVideoReset();\r
193 \r
194   PsndReset(); // pal must be known here\r
195 \r
196   // create an empty "dma" to cause 68k exec start at random frame location\r
197   Pico.t.m68c_line_start = Pico.t.m68c_aim;\r
198   PicoVideoFIFOWrite(rand() & 0x1fff, 0, 0, PVS_CPURD);\r
199 \r
200   SekFinishIdleDet();\r
201 \r
202   if (PicoIn.opt & POPT_EN_32X)\r
203     PicoReset32x();\r
204 \r
205   if (PicoIn.AHW & PAHW_MCD) {\r
206     PicoResetMCD();\r
207     return 0;\r
208   }\r
209 \r
210   // reinit, so that checksum checks pass\r
211   if (!(PicoIn.opt & POPT_DIS_IDLE_DET))\r
212     SekInitIdleDet();\r
213 \r
214   // reset sram state; enable sram access by default if it doesn't overlap with ROM\r
215   Pico.m.sram_reg = 0;\r
216   if ((Pico.sv.flags & SRF_EEPROM) || Pico.romsize <= Pico.sv.start)\r
217     Pico.m.sram_reg |= SRR_MAPPED;\r
218 \r
219   if (Pico.sv.flags & SRF_ENABLED)\r
220     elprintf(EL_STATUS, "sram: %06x - %06x; eeprom: %i", Pico.sv.start, Pico.sv.end,\r
221       !!(Pico.sv.flags & SRF_EEPROM));\r
222 \r
223   return 0;\r
224 }\r
225 \r
226 // flush config changes before emu loop starts\r
227 void PicoLoopPrepare(void)\r
228 {\r
229   if (PicoIn.regionOverride)\r
230     // force setting possibly changed..\r
231     Pico.m.pal = (PicoIn.regionOverride == 2 || PicoIn.regionOverride == 8) ? 1 : 0;\r
232 \r
233   if (Pico.m.pal) {\r
234     Pico.t.vcnt_wrap = 0x103;\r
235     Pico.t.vcnt_adj = 57;\r
236   }\r
237   else {\r
238     Pico.t.vcnt_wrap = 0xEB;\r
239     Pico.t.vcnt_adj = 6;\r
240   }\r
241 \r
242   Pico.t.m68c_line_start = Pico.t.m68c_aim; // for VDP slot calculation\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   PicoFrameStart();\r
301   PicoFrameHints();\r
302 \r
303 end:\r
304   pprof_end(frame);\r
305 }\r
306 \r
307 void PicoFrameDrawOnly(void)\r
308 {\r
309   if (!(PicoIn.AHW & PAHW_SMS)) {\r
310     PicoFrameStart();\r
311     PicoDrawSync(Pico.m.pal?239:223, 0, 0);\r
312   } else {\r
313     PicoFrameDrawOnlyMS();\r
314   }\r
315 }\r
316 \r
317 void PicoGetInternal(pint_t which, pint_ret_t *r)\r
318 {\r
319   switch (which)\r
320   {\r
321     case PI_ROM:         r->vptr = Pico.rom; break;\r
322     case PI_ISPAL:       r->vint = Pico.m.pal; break;\r
323     case PI_IS40_CELL:   r->vint = Pico.video.reg[12]&1; break;\r
324     case PI_IS240_LINES: r->vint = Pico.m.pal && (Pico.video.reg[1]&8); break;\r
325   }\r
326 }\r
327 \r
328 // vim:ts=2:sw=2:expandtab\r