83ff19ec |
1 | /* |
cff531af |
2 | * PicoDrive |
65514d85 |
3 | * (C) notaz, 2009,2010,2013 |
cff531af |
4 | * |
5 | * This work is licensed under the terms of MAME license. |
6 | * See COPYING file in the top-level directory. |
7 | * |
83ff19ec |
8 | * Register map: |
9 | * a15100 F....... R.....EA F.....AC N...VHMP 4000 // Fm Ren nrEs Aden Cart heN V H cMd Pwm |
10 | * a15102 ........ ......SM ? 4002 // intS intM |
11 | * a15104 ........ ......10 ........ hhhhhhhh 4004 // bk1 bk0 Hint |
12 | * a15106 F....... .....SDR UE...... .....SDR 4006 // Full 68S Dma Rv fUll[fb] Empt[fb] |
13 | * a15108 (32bit DREQ src) 4008 |
14 | * a1510c (32bit DREQ dst) 400c |
15 | * a15110 llllllll llllll00 4010 // DREQ Len |
16 | * a15112 (16bit FIFO reg) 4012 |
17 | * a15114 ? (16bit VRES clr) 4014 |
18 | * a15116 ? (16bit Vint clr) 4016 |
19 | * a15118 ? (16bit Hint clr) 4018 |
20 | * a1511a ........ .......C (16bit CMD clr) 401a // Cm |
21 | * a1511c ? (16bit PWM clr) 401c |
22 | * a1511e ? ? 401e |
23 | * a15120 (16 bytes comm) 2020 |
24 | * a15130 (PWM) 2030 |
65514d85 |
25 | * |
26 | * SH2 addr lines: |
27 | * iii. .cc. ..xx * // Internal, Cs, x |
28 | * |
29 | * sh2 map, wait/bus cycles (from docs): |
30 | * r w |
31 | * rom 0000000-0003fff 1 - |
32 | * sys reg 0004000-00040ff 1 1 |
33 | * vdp reg 0004100-00041ff 5 5 |
34 | * vdp pal 0004200-00043ff 5 5 |
35 | * rom 2000000-23fffff 6-15 |
36 | * dram/fb 4000000-401ffff 5-12 1-3 |
37 | * fb ovr 4020000-403ffff |
38 | * sdram 6000000-603ffff 12 2 (cycles) |
39 | * d.a. c0000000-? |
83ff19ec |
40 | */ |
be2c4208 |
41 | #include "../pico_int.h" |
42 | #include "../memory.h" |
f4bb5d6b |
43 | #include "../../cpu/sh2/compiler.h" |
be2c4208 |
44 | |
45 | static const char str_mars[] = "MARS"; |
46 | |
83ff19ec |
47 | void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s; |
974fdb5b |
48 | struct Pico32xMem *Pico32xMem; |
49 | |
5e49c3a8 |
50 | static void bank_switch(int b); |
51 | |
266c6afa |
52 | // poll detection |
19886062 |
53 | #define POLL_THRESHOLD 3 |
4ea707e1 |
54 | |
19886062 |
55 | static struct { |
56 | u32 addr, cycles; |
57 | int cnt; |
58 | } m68k_poll; |
266c6afa |
59 | |
19886062 |
60 | static int m68k_poll_detect(u32 a, u32 cycles, u32 flags) |
266c6afa |
61 | { |
19886062 |
62 | int ret = 0; |
63 | |
64 | if (a - 2 <= m68k_poll.addr && m68k_poll.addr <= a + 2 |
65 | && cycles - m68k_poll.cycles <= 64) |
66 | { |
67 | if (m68k_poll.cnt++ > POLL_THRESHOLD) { |
68 | if (!(Pico32x.emu_flags & flags)) { |
69 | elprintf(EL_32X, "m68k poll addr %08x, cyc %u", |
70 | a, cycles - m68k_poll.cycles); |
266c6afa |
71 | ret = 1; |
72 | } |
19886062 |
73 | Pico32x.emu_flags |= flags; |
266c6afa |
74 | } |
75 | } |
c987bb5c |
76 | else { |
19886062 |
77 | m68k_poll.cnt = 0; |
78 | m68k_poll.addr = a; |
c987bb5c |
79 | } |
19886062 |
80 | m68k_poll.cycles = cycles; |
266c6afa |
81 | |
82 | return ret; |
83 | } |
84 | |
19886062 |
85 | void p32x_m68k_poll_event(u32 flags) |
86 | { |
87 | if (Pico32x.emu_flags & flags) { |
88 | elprintf(EL_32X, "m68k poll %02x -> %02x", Pico32x.emu_flags, |
89 | Pico32x.emu_flags & ~flags); |
90 | Pico32x.emu_flags &= ~flags; |
91 | SekSetStop(0); |
92 | } |
93 | m68k_poll.addr = m68k_poll.cnt = 0; |
94 | } |
95 | |
96 | static void sh2_poll_detect(SH2 *sh2, u32 a, u32 flags) |
266c6afa |
97 | { |
19886062 |
98 | int cycles_left = sh2_cycles_left(sh2); |
99 | |
100 | if (a == sh2->poll_addr && sh2->poll_cycles - cycles_left <= 10) { |
101 | if (sh2->poll_cnt++ > 3) { |
102 | if (!(sh2->state & flags)) |
103 | elprintf(EL_32X, "%csh2 state: %02x->%02x", sh2->is_slave?'s':'m', |
104 | sh2->state, sh2->state | flags); |
105 | |
106 | sh2->state |= flags; |
107 | sh2_end_run(sh2, 1); |
108 | pevt_log_sh2(sh2, EVT_POLL_START); |
109 | return; |
110 | } |
111 | } |
be20816c |
112 | else |
19886062 |
113 | sh2->poll_cnt = 0; |
114 | sh2->poll_addr = a; |
115 | sh2->poll_cycles = cycles_left; |
116 | } |
117 | |
118 | void p32x_sh2_poll_event(SH2 *sh2, u32 flags, u32 m68k_cycles) |
119 | { |
120 | if (sh2->state & flags) { |
121 | elprintf(EL_32X, "%csh2 state: %02x->%02x", sh2->is_slave?'s':'m', |
122 | sh2->state, sh2->state & ~flags); |
123 | |
124 | if (sh2->m68krcycles_done < m68k_cycles) |
125 | sh2->m68krcycles_done = m68k_cycles; |
126 | |
127 | pevt_log_sh2_o(sh2, EVT_POLL_END); |
be20816c |
128 | } |
19886062 |
129 | |
130 | sh2->state &= ~flags; |
131 | sh2->poll_addr = sh2->poll_cycles = sh2->poll_cnt = 0; |
266c6afa |
132 | } |
133 | |
19886062 |
134 | static void sh2s_sync_on_read(SH2 *sh2) |
4ea707e1 |
135 | { |
19886062 |
136 | int cycles; |
137 | if (sh2->poll_cnt != 0) |
138 | return; |
139 | |
140 | cycles = sh2_cycles_done(sh2); |
141 | if (cycles > 600) |
142 | p32x_sync_other_sh2(sh2, sh2->m68krcycles_done + cycles / 3); |
4ea707e1 |
143 | } |
144 | |
974fdb5b |
145 | // SH2 faking |
b78efee2 |
146 | //#define FAKE_SH2 |
acd35d4c |
147 | #ifdef FAKE_SH2 |
27e26273 |
148 | static int p32x_csum_faked; |
974fdb5b |
149 | static const u16 comm_fakevals[] = { |
150 | 0x4d5f, 0x4f4b, // M_OK |
151 | 0x535f, 0x4f4b, // S_OK |
5e49c3a8 |
152 | 0x4D41, 0x5346, // MASF - Brutal Unleashed |
153 | 0x5331, 0x4d31, // Darxide |
154 | 0x5332, 0x4d32, |
155 | 0x5333, 0x4d33, |
156 | 0x0000, 0x0000, // eq for doom |
974fdb5b |
157 | 0x0002, // Mortal Kombat |
acd35d4c |
158 | // 0, // pad |
be2c4208 |
159 | }; |
acd35d4c |
160 | |
161 | static u32 sh2_comm_faker(u32 a) |
162 | { |
163 | static int f = 0; |
164 | if (a == 0x28 && !p32x_csum_faked) { |
165 | p32x_csum_faked = 1; |
166 | return *(unsigned short *)(Pico.rom + 0x18e); |
167 | } |
168 | if (f >= sizeof(comm_fakevals) / sizeof(comm_fakevals[0])) |
169 | f = 0; |
170 | return comm_fakevals[f++]; |
171 | } |
172 | #endif |
be2c4208 |
173 | |
4ea707e1 |
174 | // DMAC handling |
df63f1a6 |
175 | struct dma_chan { |
176 | unsigned int sar, dar; // src, dst addr |
177 | unsigned int tcr; // transfer count |
178 | unsigned int chcr; // chan ctl |
179 | // -- dm dm sm sm ts ts ar am al ds dl tb ta ie te de |
180 | // ts - transfer size: 1, 2, 4, 16 bytes |
181 | // ar - auto request if 1, else dreq signal |
182 | // ie - irq enable |
183 | // te - transfer end |
184 | // de - dma enable |
185 | #define DMA_AR (1 << 9) |
186 | #define DMA_IE (1 << 2) |
187 | #define DMA_TE (1 << 1) |
188 | #define DMA_DE (1 << 0) |
189 | }; |
190 | |
191 | struct dmac { |
192 | struct dma_chan chan[2]; |
193 | unsigned int vcrdma0; |
194 | unsigned int unknown0; |
195 | unsigned int vcrdma1; |
196 | unsigned int unknown1; |
4ea707e1 |
197 | unsigned int dmaor; |
df63f1a6 |
198 | // -- pr ae nmif dme |
199 | // pr - priority: chan0 > chan1 or round-robin |
200 | // ae - address error |
201 | // nmif - nmi occurred |
202 | // dme - DMA master enable |
203 | #define DMA_DME (1 << 0) |
204 | }; |
205 | |
206 | static void dmac_te_irq(SH2 *sh2, struct dma_chan *chan) |
207 | { |
208 | char *regs = (void *)Pico32xMem->sh2_peri_regs[sh2->is_slave]; |
209 | struct dmac *dmac = (void *)(regs + 0x180); |
210 | int level = PREG8(regs, 0xe2) & 0x0f; // IPRA |
211 | int vector = (chan == &dmac->chan[0]) ? |
212 | dmac->vcrdma0 : dmac->vcrdma1; |
213 | |
214 | elprintf(EL_32X, "dmac irq %d %d", level, vector); |
215 | sh2_internal_irq(sh2, level, vector & 0x7f); |
216 | } |
217 | |
218 | static void dmac_transfer_complete(SH2 *sh2, struct dma_chan *chan) |
219 | { |
220 | chan->chcr |= DMA_TE; // DMA has ended normally |
221 | |
222 | p32x_sh2_poll_event(sh2, SH2_STATE_SLEEP, SekCyclesDoneT()); |
223 | if (chan->chcr & DMA_IE) |
224 | dmac_te_irq(sh2, chan); |
225 | } |
226 | |
227 | static void dmac_transfer_one(SH2 *sh2, struct dma_chan *chan) |
228 | { |
229 | u32 size, d; |
230 | |
231 | size = (chan->chcr >> 10) & 3; |
232 | switch (size) { |
233 | case 0: |
234 | d = p32x_sh2_read8(chan->sar, sh2); |
235 | p32x_sh2_write8(chan->dar, d, sh2); |
236 | case 1: |
237 | d = p32x_sh2_read16(chan->sar, sh2); |
238 | p32x_sh2_write16(chan->dar, d, sh2); |
239 | break; |
240 | case 2: |
241 | d = p32x_sh2_read32(chan->sar, sh2); |
242 | p32x_sh2_write32(chan->dar, d, sh2); |
243 | break; |
244 | case 3: |
245 | elprintf(EL_32X|EL_ANOMALY, "TODO: 16byte DMA"); |
246 | chan->sar += 16; // always? |
247 | chan->tcr -= 4; |
248 | return; |
249 | } |
250 | chan->tcr--; |
251 | |
252 | size = 1 << size; |
253 | if (chan->chcr & (1 << 15)) |
254 | chan->dar -= size; |
255 | if (chan->chcr & (1 << 14)) |
256 | chan->dar += size; |
257 | if (chan->chcr & (1 << 13)) |
258 | chan->sar -= size; |
259 | if (chan->chcr & (1 << 12)) |
260 | chan->sar += size; |
261 | } |
4ea707e1 |
262 | |
df63f1a6 |
263 | static void dreq0_do(SH2 *sh2, struct dma_chan *chan) |
4ea707e1 |
264 | { |
265 | unsigned short *dreqlen = &Pico32x.regs[0x10 / 2]; |
266 | int i; |
267 | |
df63f1a6 |
268 | // debug/sanity checks |
269 | if (chan->tcr != *dreqlen) |
270 | elprintf(EL_32X|EL_ANOMALY, "dreq0: tcr0 and len differ: %d != %d", |
271 | chan->tcr, *dreqlen); |
272 | // note: DACK is not connected, single addr mode should not be used |
273 | if ((chan->chcr & 0x3f08) != 0x0400) |
274 | elprintf(EL_32X|EL_ANOMALY, "dreq0: bad control: %04x", chan->chcr); |
275 | if (chan->sar != 0x20004012) |
276 | elprintf(EL_32X|EL_ANOMALY, "dreq0: bad sar?: %08x\n", chan->sar); |
4ea707e1 |
277 | |
1b3f5844 |
278 | // HACK: assume bus is busy and SH2 is halted |
df63f1a6 |
279 | sh2->state |= SH2_STATE_SLEEP; |
280 | |
281 | for (i = 0; i < Pico32x.dmac0_fifo_ptr && chan->tcr > 0; i++) { |
282 | elprintf(EL_32X, "dmaw [%08x] %04x, left %d", |
283 | chan->dar, Pico32x.dmac_fifo[i], *dreqlen); |
284 | p32x_sh2_write16(chan->dar, Pico32x.dmac_fifo[i], sh2); |
285 | chan->dar += 2; |
286 | chan->tcr--; |
4ea707e1 |
287 | (*dreqlen)--; |
288 | } |
289 | |
df63f1a6 |
290 | if (Pico32x.dmac0_fifo_ptr != i) |
291 | memmove(Pico32x.dmac_fifo, &Pico32x.dmac_fifo[i], |
292 | (Pico32x.dmac0_fifo_ptr - i) * 2); |
293 | Pico32x.dmac0_fifo_ptr -= i; |
294 | |
4ea707e1 |
295 | Pico32x.regs[6 / 2] &= ~P32XS_FULL; |
296 | if (*dreqlen == 0) |
297 | Pico32x.regs[6 / 2] &= ~P32XS_68S; // transfer complete |
df63f1a6 |
298 | if (chan->tcr == 0) |
299 | dmac_transfer_complete(sh2, chan); |
300 | else |
301 | sh2_end_run(sh2, 16); |
302 | } |
303 | |
304 | static void dreq1_do(SH2 *sh2, struct dma_chan *chan) |
305 | { |
306 | // debug/sanity checks |
307 | if ((chan->chcr & 0xc308) != 0x0000) |
308 | elprintf(EL_32X|EL_ANOMALY, "dreq1: bad control: %04x", chan->chcr); |
309 | if ((chan->dar & ~0xf) != 0x20004030) |
310 | elprintf(EL_32X|EL_ANOMALY, "dreq1: bad dar?: %08x\n", chan->dar); |
311 | |
312 | dmac_transfer_one(sh2, chan); |
313 | if (chan->tcr == 0) |
314 | dmac_transfer_complete(sh2, chan); |
315 | } |
316 | |
317 | static void dreq0_trigger(void) |
318 | { |
319 | struct dmac *mdmac = (void *)&Pico32xMem->sh2_peri_regs[0][0x180 / 4]; |
320 | struct dmac *sdmac = (void *)&Pico32xMem->sh2_peri_regs[1][0x180 / 4]; |
321 | |
322 | elprintf(EL_32X, "dreq0_trigger\n"); |
323 | if ((mdmac->dmaor & DMA_DME) && (mdmac->chan[0].chcr & 3) == DMA_DE) { |
324 | dreq0_do(&msh2, &mdmac->chan[0]); |
325 | } |
326 | if ((sdmac->dmaor & DMA_DME) && (sdmac->chan[0].chcr & 3) == DMA_DE) { |
327 | dreq0_do(&ssh2, &sdmac->chan[0]); |
be20816c |
328 | } |
4ea707e1 |
329 | } |
330 | |
df63f1a6 |
331 | void p32x_dreq1_trigger(void) |
332 | { |
333 | struct dmac *mdmac = (void *)&Pico32xMem->sh2_peri_regs[0][0x180 / 4]; |
334 | struct dmac *sdmac = (void *)&Pico32xMem->sh2_peri_regs[1][0x180 / 4]; |
335 | int hit = 0; |
336 | |
337 | elprintf(EL_32X, "dreq1_trigger\n"); |
338 | if ((mdmac->dmaor & DMA_DME) && (mdmac->chan[1].chcr & 3) == DMA_DE) { |
339 | dreq1_do(&msh2, &mdmac->chan[1]); |
340 | hit = 1; |
341 | } |
342 | if ((sdmac->dmaor & DMA_DME) && (sdmac->chan[1].chcr & 3) == DMA_DE) { |
343 | dreq1_do(&ssh2, &sdmac->chan[1]); |
344 | hit = 1; |
345 | } |
346 | |
347 | if (!hit) |
348 | elprintf(EL_32X|EL_ANOMALY, "dreq1: nobody cared"); |
349 | } |
350 | |
351 | // DMA trigger by SH2 register write |
352 | static void dmac_trigger(SH2 *sh2, struct dma_chan *chan) |
353 | { |
354 | elprintf(EL_32X, "sh2 DMA %08x->%08x, cnt %d, chcr %04x @%06x", |
355 | chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc); |
356 | chan->tcr &= 0xffffff; |
357 | |
358 | if (chan->chcr & DMA_AR) { |
359 | // auto-request transfer |
360 | while ((int)chan->tcr > 0) |
361 | dmac_transfer_one(sh2, chan); |
362 | dmac_transfer_complete(sh2, chan); |
363 | return; |
364 | } |
365 | |
366 | // DREQ0 is only sent after first 4 words are written. |
367 | // we do multiple of 4 words to avoid messing up alignment |
368 | if (chan->sar == 0x20004012) { |
369 | if (Pico32x.dmac0_fifo_ptr && (Pico32x.dmac0_fifo_ptr & 3) == 0) { |
370 | elprintf(EL_32X, "68k -> sh2 DMA"); |
371 | dreq0_trigger(); |
372 | } |
373 | return; |
374 | } |
375 | |
376 | elprintf(EL_32X|EL_ANOMALY, "unhandled DMA: " |
377 | "%08x->%08x, cnt %d, chcr %04x @%06x", |
378 | chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc); |
379 | } |
380 | |
4ea707e1 |
381 | // ------------------------------------------------------------------ |
b78efee2 |
382 | // 68k regs |
4ea707e1 |
383 | |
be2c4208 |
384 | static u32 p32x_reg_read16(u32 a) |
385 | { |
386 | a &= 0x3e; |
387 | |
3cf9570b |
388 | #if 0 |
974fdb5b |
389 | if ((a & 0x30) == 0x20) |
acd35d4c |
390 | return sh2_comm_faker(a); |
266c6afa |
391 | #else |
5fadfb1c |
392 | if ((a & 0x30) == 0x20) { |
5fadfb1c |
393 | static u32 dr2 = 0; |
a8fd6e37 |
394 | unsigned int cycles = SekCyclesDoneT(); |
395 | int comreg = 1 << (a & 0x0f) / 2; |
396 | |
397 | // evil X-Men proto polls in a dbra loop and expects it to expire.. |
5fadfb1c |
398 | if (SekDar(2) != dr2) |
399 | m68k_poll.cnt = 0; |
400 | dr2 = SekDar(2); |
401 | |
a8fd6e37 |
402 | if (cycles - msh2.m68krcycles_done > 500) |
403 | p32x_sync_sh2s(cycles); |
404 | if (Pico32x.comm_dirty_sh2 & comreg) |
405 | Pico32x.comm_dirty_sh2 &= ~comreg; |
19886062 |
406 | else if (m68k_poll_detect(a, cycles, P32XF_68KCPOLL)) { |
5fadfb1c |
407 | SekSetStop(1); |
408 | SekEndTimeslice(16); |
409 | } |
410 | dr2 = SekDar(2); |
a8fd6e37 |
411 | goto out; |
266c6afa |
412 | } |
acd35d4c |
413 | #endif |
87accdf7 |
414 | |
a8fd6e37 |
415 | if (a == 2) { // INTM, INTS |
416 | unsigned int cycles = SekCyclesDoneT(); |
417 | if (cycles - msh2.m68krcycles_done > 64) |
418 | p32x_sync_sh2s(cycles); |
419 | return ((Pico32x.sh2irqi[0] & P32XI_CMD) >> 4) | ((Pico32x.sh2irqi[1] & P32XI_CMD) >> 3); |
420 | } |
421 | |
db1d3564 |
422 | if ((a & 0x30) == 0x30) |
df63f1a6 |
423 | return p32x_pwm_read16(a, SekCyclesDoneT()); |
974fdb5b |
424 | |
a8fd6e37 |
425 | out: |
be2c4208 |
426 | return Pico32x.regs[a / 2]; |
427 | } |
428 | |
be2c4208 |
429 | static void p32x_reg_write8(u32 a, u32 d) |
430 | { |
acd35d4c |
431 | u16 *r = Pico32x.regs; |
be2c4208 |
432 | a &= 0x3f; |
433 | |
97d3f47f |
434 | // for things like bset on comm port |
435 | m68k_poll.cnt = 0; |
436 | |
acd35d4c |
437 | switch (a) { |
4ea707e1 |
438 | case 0: // adapter ctl |
83ff19ec |
439 | r[0] = (r[0] & ~P32XS_FM) | ((d << 8) & P32XS_FM); |
440 | return; |
441 | case 1: // adapter ctl, RES bit writeable |
442 | if ((d ^ r[0]) & d & P32XS_nRES) |
443 | p32x_reset_sh2s(); |
444 | r[0] = (r[0] & ~P32XS_nRES) | (d & P32XS_nRES); |
1b3f5844 |
445 | return; |
4ea707e1 |
446 | case 3: // irq ctl |
447 | if ((d & 1) && !(Pico32x.sh2irqi[0] & P32XI_CMD)) { |
a8fd6e37 |
448 | p32x_sync_sh2s(SekCyclesDoneT()); |
4ea707e1 |
449 | Pico32x.sh2irqi[0] |= P32XI_CMD; |
19886062 |
450 | p32x_update_irls(NULL); |
4ea707e1 |
451 | } |
b78efee2 |
452 | if ((d & 2) && !(Pico32x.sh2irqi[1] & P32XI_CMD)) { |
a8fd6e37 |
453 | p32x_sync_sh2s(SekCyclesDoneT()); |
b78efee2 |
454 | Pico32x.sh2irqi[1] |= P32XI_CMD; |
19886062 |
455 | p32x_update_irls(NULL); |
b78efee2 |
456 | } |
1b3f5844 |
457 | return; |
4ea707e1 |
458 | case 5: // bank |
acd35d4c |
459 | d &= 7; |
4ea707e1 |
460 | if (r[4 / 2] != d) { |
461 | r[4 / 2] = d; |
acd35d4c |
462 | bank_switch(d); |
463 | } |
1b3f5844 |
464 | return; |
4ea707e1 |
465 | case 7: // DREQ ctl |
97d3f47f |
466 | r[6 / 2] = (r[6 / 2] & P32XS_FULL) | (d & (P32XS_68S|P32XS_DMA|P32XS_RV)); |
1b3f5844 |
467 | return; |
87accdf7 |
468 | case 0x1b: // TV |
469 | r[0x1a / 2] = d; |
1b3f5844 |
470 | return; |
471 | } |
472 | |
473 | if ((a & 0x30) == 0x20) { |
474 | u8 *r8 = (u8 *)r; |
a8fd6e37 |
475 | int cycles = SekCyclesDoneT(); |
476 | int comreg; |
477 | |
478 | if (r8[a ^ 1] == d) |
479 | return; |
19886062 |
480 | |
a8fd6e37 |
481 | comreg = 1 << (a & 0x0f) / 2; |
482 | if (Pico32x.comm_dirty_68k & comreg) |
483 | p32x_sync_sh2s(cycles); |
484 | |
1b3f5844 |
485 | r8[a ^ 1] = d; |
19886062 |
486 | p32x_sh2_poll_event(&sh2s[0], SH2_STATE_CPOLL, cycles); |
487 | p32x_sh2_poll_event(&sh2s[1], SH2_STATE_CPOLL, cycles); |
a8fd6e37 |
488 | Pico32x.comm_dirty_68k |= comreg; |
489 | |
490 | if (cycles - (int)msh2.m68krcycles_done > 120) |
491 | p32x_sync_sh2s(cycles); |
1b3f5844 |
492 | return; |
5e49c3a8 |
493 | } |
494 | } |
495 | |
496 | static void p32x_reg_write16(u32 a, u32 d) |
497 | { |
acd35d4c |
498 | u16 *r = Pico32x.regs; |
499 | a &= 0x3e; |
500 | |
97d3f47f |
501 | // for things like bset on comm port |
502 | m68k_poll.cnt = 0; |
503 | |
acd35d4c |
504 | switch (a) { |
4ea707e1 |
505 | case 0x00: // adapter ctl |
83ff19ec |
506 | if ((d ^ r[0]) & d & P32XS_nRES) |
507 | p32x_reset_sh2s(); |
508 | r[0] = (r[0] & ~(P32XS_FM|P32XS_nRES)) | (d & (P32XS_FM|P32XS_nRES)); |
acd35d4c |
509 | return; |
4ea707e1 |
510 | case 0x10: // DREQ len |
511 | r[a / 2] = d & ~3; |
512 | return; |
513 | case 0x12: // FIFO reg |
514 | if (!(r[6 / 2] & P32XS_68S)) { |
515 | elprintf(EL_32X|EL_ANOMALY, "DREQ FIFO w16 without 68S?"); |
5609d343 |
516 | return; |
4ea707e1 |
517 | } |
df63f1a6 |
518 | if (Pico32x.dmac0_fifo_ptr < DMAC_FIFO_LEN) { |
519 | Pico32x.dmac_fifo[Pico32x.dmac0_fifo_ptr++] = d; |
520 | if ((Pico32x.dmac0_fifo_ptr & 3) == 0) |
521 | dreq0_trigger(); |
522 | if (Pico32x.dmac0_fifo_ptr == DMAC_FIFO_LEN) |
4ea707e1 |
523 | r[6 / 2] |= P32XS_FULL; |
524 | } |
525 | break; |
acd35d4c |
526 | } |
527 | |
4ea707e1 |
528 | // DREQ src, dst |
529 | if ((a & 0x38) == 0x08) { |
530 | r[a / 2] = d; |
531 | return; |
532 | } |
533 | // comm port |
a8fd6e37 |
534 | else if ((a & 0x30) == 0x20) { |
535 | int cycles = SekCyclesDoneT(); |
536 | int comreg; |
537 | |
538 | if (r[a / 2] == d) |
539 | return; |
540 | |
541 | comreg = 1 << (a & 0x0f) / 2; |
542 | if (Pico32x.comm_dirty_68k & comreg) |
543 | p32x_sync_sh2s(cycles); |
544 | |
acd35d4c |
545 | r[a / 2] = d; |
19886062 |
546 | p32x_sh2_poll_event(&sh2s[0], SH2_STATE_CPOLL, cycles); |
547 | p32x_sh2_poll_event(&sh2s[1], SH2_STATE_CPOLL, cycles); |
a8fd6e37 |
548 | Pico32x.comm_dirty_68k |= comreg; |
549 | |
550 | if (cycles - (int)msh2.m68krcycles_done > 120) |
551 | p32x_sync_sh2s(cycles); |
acd35d4c |
552 | return; |
553 | } |
db1d3564 |
554 | // PWM |
555 | else if ((a & 0x30) == 0x30) { |
df63f1a6 |
556 | p32x_pwm_write16(a, d, SekCyclesDoneT()); |
db1d3564 |
557 | return; |
558 | } |
acd35d4c |
559 | |
5e49c3a8 |
560 | p32x_reg_write8(a + 1, d); |
be2c4208 |
561 | } |
562 | |
4ea707e1 |
563 | // ------------------------------------------------------------------ |
be2c4208 |
564 | // VDP regs |
565 | static u32 p32x_vdp_read16(u32 a) |
566 | { |
567 | a &= 0x0e; |
568 | |
569 | return Pico32x.vdp_regs[a / 2]; |
570 | } |
571 | |
be2c4208 |
572 | static void p32x_vdp_write8(u32 a, u32 d) |
573 | { |
974fdb5b |
574 | u16 *r = Pico32x.vdp_regs; |
be2c4208 |
575 | a &= 0x0f; |
576 | |
974fdb5b |
577 | // TODO: verify what's writeable |
be2c4208 |
578 | switch (a) { |
974fdb5b |
579 | case 0x01: |
5e49c3a8 |
580 | // priority inversion is handled in palette |
581 | if ((r[0] ^ d) & P32XV_PRI) |
582 | Pico32x.dirty_pal = 1; |
974fdb5b |
583 | r[0] = (r[0] & P32XV_nPAL) | (d & 0xff); |
be20816c |
584 | break; |
e51e5983 |
585 | case 0x03: // shift (for pp mode) |
586 | r[2 / 2] = d & 1; |
587 | break; |
be20816c |
588 | case 0x05: // fill len |
589 | r[4 / 2] = d & 0xff; |
974fdb5b |
590 | break; |
be2c4208 |
591 | case 0x0b: |
974fdb5b |
592 | d &= 1; |
593 | Pico32x.pending_fb = d; |
594 | // if we are blanking and FS bit is changing |
4ea707e1 |
595 | if (((r[0x0a/2] & P32XV_VBLK) || (r[0] & P32XV_Mx) == 0) && ((r[0x0a/2] ^ d) & P32XV_FS)) { |
b4db550e |
596 | r[0x0a/2] ^= P32XV_FS; |
5609d343 |
597 | Pico32xSwapDRAM(d ^ 1); |
266c6afa |
598 | elprintf(EL_32X, "VDP FS: %d", r[0x0a/2] & P32XV_FS); |
be2c4208 |
599 | } |
600 | break; |
601 | } |
602 | } |
603 | |
19886062 |
604 | static void p32x_vdp_write16(u32 a, u32 d, SH2 *sh2) |
974fdb5b |
605 | { |
be20816c |
606 | a &= 0x0e; |
607 | if (a == 6) { // fill start |
608 | Pico32x.vdp_regs[6 / 2] = d; |
609 | return; |
610 | } |
611 | if (a == 8) { // fill data |
612 | u16 *dram = Pico32xMem->dram[(Pico32x.vdp_regs[0x0a/2] & P32XV_FS) ^ 1]; |
1b3f5844 |
613 | int len = Pico32x.vdp_regs[4 / 2] + 1; |
a8fd6e37 |
614 | int len1 = len; |
be20816c |
615 | a = Pico32x.vdp_regs[6 / 2]; |
a8fd6e37 |
616 | while (len1--) { |
be20816c |
617 | dram[a] = d; |
618 | a = (a & 0xff00) | ((a + 1) & 0xff); |
619 | } |
a8fd6e37 |
620 | Pico32x.vdp_regs[0x06 / 2] = a; |
621 | Pico32x.vdp_regs[0x08 / 2] = d; |
19886062 |
622 | if (sh2 != NULL && len > 4) { |
a8fd6e37 |
623 | Pico32x.vdp_regs[0x0a / 2] |= P32XV_nFEN; |
19886062 |
624 | // supposedly takes 3 bus/6 sh2 cycles? or 3 sh2 cycles? |
625 | p32x_event_schedule_sh2(sh2, P32X_EVENT_FILLEND, 3 + len); |
a8fd6e37 |
626 | } |
be20816c |
627 | return; |
628 | } |
629 | |
974fdb5b |
630 | p32x_vdp_write8(a | 1, d); |
631 | } |
632 | |
4ea707e1 |
633 | // ------------------------------------------------------------------ |
acd35d4c |
634 | // SH2 regs |
b78efee2 |
635 | |
636 | static u32 p32x_sh2reg_read16(u32 a, int cpuid) |
acd35d4c |
637 | { |
4ea707e1 |
638 | u16 *r = Pico32x.regs; |
639 | a &= 0xfe; // ? |
266c6afa |
640 | |
4ea707e1 |
641 | switch (a) { |
642 | case 0x00: // adapter/irq ctl |
87accdf7 |
643 | return (r[0] & P32XS_FM) | Pico32x.sh2_regs[0] | Pico32x.sh2irq_mask[cpuid]; |
c987bb5c |
644 | case 0x04: // H count (often as comm too) |
19886062 |
645 | sh2_poll_detect(&sh2s[cpuid], a, SH2_STATE_CPOLL); |
646 | sh2s_sync_on_read(&sh2s[cpuid]); |
87accdf7 |
647 | return Pico32x.sh2_regs[4 / 2]; |
4ea707e1 |
648 | case 0x10: // DREQ len |
649 | return r[a / 2]; |
acd35d4c |
650 | } |
4ea707e1 |
651 | |
db1d3564 |
652 | // DREQ src, dst |
653 | if ((a & 0x38) == 0x08) |
4ea707e1 |
654 | return r[a / 2]; |
db1d3564 |
655 | // comm port |
656 | if ((a & 0x30) == 0x20) { |
a8fd6e37 |
657 | int comreg = 1 << (a & 0x0f) / 2; |
658 | if (Pico32x.comm_dirty_68k & comreg) |
659 | Pico32x.comm_dirty_68k &= ~comreg; |
19886062 |
660 | else |
661 | sh2_poll_detect(&sh2s[cpuid], a, SH2_STATE_CPOLL); |
662 | sh2s_sync_on_read(&sh2s[cpuid]); |
db1d3564 |
663 | return r[a / 2]; |
664 | } |
665 | if ((a & 0x30) == 0x30) { |
df63f1a6 |
666 | return p32x_pwm_read16(a, sh2_cycles_done_m68k(&sh2s[cpuid])); |
db1d3564 |
667 | } |
acd35d4c |
668 | |
669 | return 0; |
670 | } |
671 | |
b78efee2 |
672 | static void p32x_sh2reg_write8(u32 a, u32 d, int cpuid) |
acd35d4c |
673 | { |
4ea707e1 |
674 | a &= 0xff; |
19886062 |
675 | |
676 | sh2s[cpuid].poll_addr = 0; |
677 | |
87accdf7 |
678 | switch (a) { |
679 | case 0: // FM |
680 | Pico32x.regs[0] &= ~P32XS_FM; |
681 | Pico32x.regs[0] |= (d << 8) & P32XS_FM; |
1b3f5844 |
682 | return; |
19886062 |
683 | case 1: // HEN/irq masks |
684 | if ((d ^ Pico32x.sh2_regs[0]) & 0x80) |
685 | elprintf(EL_ANOMALY|EL_32X, "HEN"); |
87accdf7 |
686 | Pico32x.sh2irq_mask[cpuid] = d & 0x8f; |
687 | Pico32x.sh2_regs[0] &= ~0x80; |
688 | Pico32x.sh2_regs[0] |= d & 0x80; |
a8fd6e37 |
689 | if (d & 1) |
19886062 |
690 | p32x_pwm_schedule_sh2(&sh2s[cpuid]); |
691 | p32x_update_irls(&sh2s[cpuid]); |
1b3f5844 |
692 | return; |
87accdf7 |
693 | case 5: // H count |
19886062 |
694 | d &= 0xff; |
695 | if (Pico32x.sh2_regs[4 / 2] != d) { |
696 | Pico32x.sh2_regs[4 / 2] = d; |
697 | p32x_sh2_poll_event(&sh2s[cpuid ^ 1], SH2_STATE_CPOLL, |
698 | sh2_cycles_done_m68k(&sh2s[cpuid])); |
699 | sh2_end_run(&sh2s[cpuid], 4); |
700 | } |
1b3f5844 |
701 | return; |
702 | } |
703 | |
704 | if ((a & 0x30) == 0x20) { |
705 | u8 *r8 = (u8 *)Pico32x.regs; |
a8fd6e37 |
706 | int comreg; |
707 | if (r8[a ^ 1] == d) |
708 | return; |
709 | |
1b3f5844 |
710 | r8[a ^ 1] = d; |
19886062 |
711 | p32x_m68k_poll_event(P32XF_68KCPOLL); |
712 | p32x_sh2_poll_event(&sh2s[cpuid ^ 1], SH2_STATE_CPOLL, |
713 | sh2_cycles_done_m68k(&sh2s[cpuid])); |
a8fd6e37 |
714 | comreg = 1 << (a & 0x0f) / 2; |
715 | Pico32x.comm_dirty_sh2 |= comreg; |
1b3f5844 |
716 | return; |
4ea707e1 |
717 | } |
acd35d4c |
718 | } |
719 | |
b78efee2 |
720 | static void p32x_sh2reg_write16(u32 a, u32 d, int cpuid) |
acd35d4c |
721 | { |
4ea707e1 |
722 | a &= 0xfe; |
acd35d4c |
723 | |
19886062 |
724 | sh2s[cpuid].poll_addr = 0; |
725 | |
db1d3564 |
726 | // comm |
a8fd6e37 |
727 | if ((a & 0x30) == 0x20) { |
728 | int comreg; |
729 | if (Pico32x.regs[a / 2] == d) |
730 | return; |
731 | |
b78efee2 |
732 | Pico32x.regs[a / 2] = d; |
19886062 |
733 | p32x_m68k_poll_event(P32XF_68KCPOLL); |
734 | p32x_sh2_poll_event(&sh2s[cpuid ^ 1], SH2_STATE_CPOLL, |
735 | sh2_cycles_done_m68k(&sh2s[cpuid])); |
a8fd6e37 |
736 | comreg = 1 << (a & 0x0f) / 2; |
737 | Pico32x.comm_dirty_sh2 |= comreg; |
acd35d4c |
738 | return; |
739 | } |
db1d3564 |
740 | // PWM |
741 | else if ((a & 0x30) == 0x30) { |
df63f1a6 |
742 | p32x_pwm_write16(a, d, sh2_cycles_done_m68k(&sh2s[cpuid])); |
db1d3564 |
743 | return; |
744 | } |
acd35d4c |
745 | |
4ea707e1 |
746 | switch (a) { |
87accdf7 |
747 | case 0: // FM |
748 | Pico32x.regs[0] &= ~P32XS_FM; |
749 | Pico32x.regs[0] |= d & P32XS_FM; |
750 | break; |
4ea707e1 |
751 | case 0x14: Pico32x.sh2irqs &= ~P32XI_VRES; goto irls; |
752 | case 0x16: Pico32x.sh2irqs &= ~P32XI_VINT; goto irls; |
753 | case 0x18: Pico32x.sh2irqs &= ~P32XI_HINT; goto irls; |
b78efee2 |
754 | case 0x1a: Pico32x.sh2irqi[cpuid] &= ~P32XI_CMD; goto irls; |
be20816c |
755 | case 0x1c: |
756 | Pico32x.sh2irqs &= ~P32XI_PWM; |
df63f1a6 |
757 | p32x_pwm_schedule_sh2(&sh2s[cpuid]); |
be20816c |
758 | goto irls; |
4ea707e1 |
759 | } |
760 | |
b78efee2 |
761 | p32x_sh2reg_write8(a | 1, d, cpuid); |
4ea707e1 |
762 | return; |
763 | |
764 | irls: |
19886062 |
765 | p32x_update_irls(&sh2s[cpuid]); |
4ea707e1 |
766 | } |
767 | |
87accdf7 |
768 | // ------------------------------------------------------------------ |
769 | // SH2 internal peripherals |
1d7a28a7 |
770 | // we keep them in little endian format |
87accdf7 |
771 | static u32 sh2_peripheral_read8(u32 a, int id) |
772 | { |
773 | u8 *r = (void *)Pico32xMem->sh2_peri_regs[id]; |
774 | u32 d; |
775 | |
776 | a &= 0x1ff; |
1d7a28a7 |
777 | d = PREG8(r, a); |
87accdf7 |
778 | |
779 | elprintf(EL_32X, "%csh2 peri r8 [%08x] %02x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id)); |
780 | return d; |
781 | } |
782 | |
1d7a28a7 |
783 | static u32 sh2_peripheral_read16(u32 a, int id) |
784 | { |
785 | u16 *r = (void *)Pico32xMem->sh2_peri_regs[id]; |
786 | u32 d; |
787 | |
788 | a &= 0x1ff; |
789 | d = r[(a / 2) ^ 1]; |
790 | |
791 | elprintf(EL_32X, "%csh2 peri r16 [%08x] %04x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id)); |
792 | return d; |
793 | } |
794 | |
87accdf7 |
795 | static u32 sh2_peripheral_read32(u32 a, int id) |
4ea707e1 |
796 | { |
797 | u32 d; |
798 | a &= 0x1fc; |
97d3f47f |
799 | d = Pico32xMem->sh2_peri_regs[id][a / 4]; |
4ea707e1 |
800 | |
97d3f47f |
801 | elprintf(EL_32X, "%csh2 peri r32 [%08x] %08x @%06x", id ? 's' : 'm', a | ~0x1ff, d, sh2_pc(id)); |
4ea707e1 |
802 | return d; |
acd35d4c |
803 | } |
804 | |
e05b81fc |
805 | static int REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, int id) |
87accdf7 |
806 | { |
807 | u8 *r = (void *)Pico32xMem->sh2_peri_regs[id]; |
808 | elprintf(EL_32X, "%csh2 peri w8 [%08x] %02x @%06x", id ? 's' : 'm', a, d, sh2_pc(id)); |
809 | |
810 | a &= 0x1ff; |
1d7a28a7 |
811 | PREG8(r, a) = d; |
812 | |
813 | // X-men SCI hack |
814 | if ((a == 2 && (d & 0x20)) || // transmiter enabled |
815 | (a == 4 && !(d & 0x80))) { // valid data in TDR |
816 | void *oregs = Pico32xMem->sh2_peri_regs[id ^ 1]; |
817 | if ((PREG8(oregs, 2) & 0x50) == 0x50) { // receiver + irq enabled |
818 | int level = PREG8(oregs, 0x60) >> 4; |
819 | int vector = PREG8(oregs, 0x63) & 0x7f; |
820 | elprintf(EL_32X, "%csh2 SCI recv irq (%d, %d)", (id ^ 1) ? 's' : 'm', level, vector); |
821 | sh2_internal_irq(&sh2s[id ^ 1], level, vector); |
e05b81fc |
822 | return 1; |
1d7a28a7 |
823 | } |
824 | } |
e05b81fc |
825 | return 0; |
1d7a28a7 |
826 | } |
827 | |
e05b81fc |
828 | static int REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, int id) |
1d7a28a7 |
829 | { |
830 | u16 *r = (void *)Pico32xMem->sh2_peri_regs[id]; |
831 | elprintf(EL_32X, "%csh2 peri w16 [%08x] %04x @%06x", id ? 's' : 'm', a, d, sh2_pc(id)); |
832 | |
833 | a &= 0x1ff; |
834 | |
835 | // evil WDT |
836 | if (a == 0x80) { |
837 | if ((d & 0xff00) == 0xa500) { // WTCSR |
838 | PREG8(r, 0x80) = d; |
839 | p32x_timers_recalc(); |
840 | } |
841 | if ((d & 0xff00) == 0x5a00) // WTCNT |
842 | PREG8(r, 0x81) = d; |
e05b81fc |
843 | return 0; |
1d7a28a7 |
844 | } |
845 | |
846 | r[(a / 2) ^ 1] = d; |
e05b81fc |
847 | return 0; |
87accdf7 |
848 | } |
849 | |
850 | static void sh2_peripheral_write32(u32 a, u32 d, int id) |
4ea707e1 |
851 | { |
be20816c |
852 | u32 *r = Pico32xMem->sh2_peri_regs[id]; |
b78efee2 |
853 | elprintf(EL_32X, "%csh2 peri w32 [%08x] %08x @%06x", id ? 's' : 'm', a, d, sh2_pc(id)); |
4ea707e1 |
854 | |
855 | a &= 0x1fc; |
856 | r[a / 4] = d; |
857 | |
97d3f47f |
858 | switch (a) { |
be20816c |
859 | // division unit (TODO: verify): |
97d3f47f |
860 | case 0x104: // DVDNT: divident L, starts divide |
861 | elprintf(EL_32X, "%csh2 divide %08x / %08x", id ? 's' : 'm', d, r[0x100 / 4]); |
862 | if (r[0x100 / 4]) { |
be20816c |
863 | signed int divisor = r[0x100 / 4]; |
864 | r[0x118 / 4] = r[0x110 / 4] = (signed int)d % divisor; |
865 | r[0x104 / 4] = r[0x11c / 4] = r[0x114 / 4] = (signed int)d / divisor; |
97d3f47f |
866 | } |
1625ed01 |
867 | else |
868 | r[0x110 / 4] = r[0x114 / 4] = r[0x118 / 4] = r[0x11c / 4] = 0; // ? |
97d3f47f |
869 | break; |
870 | case 0x114: |
871 | elprintf(EL_32X, "%csh2 divide %08x%08x / %08x @%08x", |
872 | id ? 's' : 'm', r[0x110 / 4], d, r[0x100 / 4], sh2_pc(id)); |
873 | if (r[0x100 / 4]) { |
be20816c |
874 | signed long long divident = (signed long long)r[0x110 / 4] << 32 | d; |
875 | signed int divisor = r[0x100 / 4]; |
97d3f47f |
876 | // XXX: undocumented mirroring to 0x118,0x11c? |
be20816c |
877 | r[0x118 / 4] = r[0x110 / 4] = divident % divisor; |
1625ed01 |
878 | divident /= divisor; |
879 | r[0x11c / 4] = r[0x114 / 4] = divident; |
880 | divident >>= 31; |
881 | if ((unsigned long long)divident + 1 > 1) { |
882 | //elprintf(EL_32X, "%csh2 divide overflow! @%08x", id ? 's' : 'm', sh2_pc(id)); |
883 | r[0x11c / 4] = r[0x114 / 4] = divident > 0 ? 0x7fffffff : 0x80000000; // overflow |
884 | } |
97d3f47f |
885 | } |
1625ed01 |
886 | else |
887 | r[0x110 / 4] = r[0x114 / 4] = r[0x118 / 4] = r[0x11c / 4] = 0; // ? |
97d3f47f |
888 | break; |
889 | } |
890 | |
df63f1a6 |
891 | // perhaps starting a DMA? |
892 | if (a == 0x1b0 || a == 0x18c || a == 0x19c) { |
893 | struct dmac *dmac = (void *)&Pico32xMem->sh2_peri_regs[id][0x180 / 4]; |
894 | if (!(dmac->dmaor & DMA_DME)) |
895 | return; |
be20816c |
896 | |
df63f1a6 |
897 | if ((dmac->chan[0].chcr & (DMA_TE|DMA_DE)) == DMA_DE) |
898 | dmac_trigger(&sh2s[id], &dmac->chan[0]); |
899 | if ((dmac->chan[1].chcr & (DMA_TE|DMA_DE)) == DMA_DE) |
900 | dmac_trigger(&sh2s[id], &dmac->chan[1]); |
4ea707e1 |
901 | } |
902 | } |
903 | |
904 | // ------------------------------------------------------------------ |
83ff19ec |
905 | // 32x handlers |
906 | |
907 | // after ADEN |
908 | static u32 PicoRead8_32x_on(u32 a) |
be2c4208 |
909 | { |
910 | u32 d = 0; |
911 | if ((a & 0xffc0) == 0x5100) { // a15100 |
912 | d = p32x_reg_read16(a); |
913 | goto out_16to8; |
914 | } |
915 | |
83ff19ec |
916 | if ((a & 0xfc00) != 0x5000) |
917 | return PicoRead8_io(a); |
974fdb5b |
918 | |
919 | if ((a & 0xfff0) == 0x5180) { // a15180 |
be2c4208 |
920 | d = p32x_vdp_read16(a); |
921 | goto out_16to8; |
922 | } |
923 | |
974fdb5b |
924 | if ((a & 0xfe00) == 0x5200) { // a15200 |
925 | d = Pico32xMem->pal[(a & 0x1ff) / 2]; |
926 | goto out_16to8; |
927 | } |
928 | |
be2c4208 |
929 | if ((a & 0xfffc) == 0x30ec) { // a130ec |
930 | d = str_mars[a & 3]; |
931 | goto out; |
932 | } |
933 | |
934 | elprintf(EL_UIO, "m68k unmapped r8 [%06x] @%06x", a, SekPc); |
935 | return d; |
936 | |
937 | out_16to8: |
938 | if (a & 1) |
939 | d &= 0xff; |
940 | else |
941 | d >>= 8; |
942 | |
943 | out: |
944 | elprintf(EL_32X, "m68k 32x r8 [%06x] %02x @%06x", a, d, SekPc); |
945 | return d; |
946 | } |
947 | |
83ff19ec |
948 | static u32 PicoRead16_32x_on(u32 a) |
be2c4208 |
949 | { |
950 | u32 d = 0; |
951 | if ((a & 0xffc0) == 0x5100) { // a15100 |
952 | d = p32x_reg_read16(a); |
953 | goto out; |
954 | } |
955 | |
83ff19ec |
956 | if ((a & 0xfc00) != 0x5000) |
957 | return PicoRead16_io(a); |
974fdb5b |
958 | |
959 | if ((a & 0xfff0) == 0x5180) { // a15180 |
be2c4208 |
960 | d = p32x_vdp_read16(a); |
961 | goto out; |
962 | } |
963 | |
974fdb5b |
964 | if ((a & 0xfe00) == 0x5200) { // a15200 |
965 | d = Pico32xMem->pal[(a & 0x1ff) / 2]; |
966 | goto out; |
967 | } |
968 | |
be2c4208 |
969 | if ((a & 0xfffc) == 0x30ec) { // a130ec |
970 | d = !(a & 2) ? ('M'<<8)|'A' : ('R'<<8)|'S'; |
971 | goto out; |
972 | } |
973 | |
974 | elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc); |
975 | return d; |
976 | |
977 | out: |
978 | elprintf(EL_32X, "m68k 32x r16 [%06x] %04x @%06x", a, d, SekPc); |
979 | return d; |
980 | } |
981 | |
83ff19ec |
982 | static void PicoWrite8_32x_on(u32 a, u32 d) |
be2c4208 |
983 | { |
984 | if ((a & 0xfc00) == 0x5000) |
985 | elprintf(EL_32X, "m68k 32x w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
986 | |
987 | if ((a & 0xffc0) == 0x5100) { // a15100 |
988 | p32x_reg_write8(a, d); |
989 | return; |
990 | } |
991 | |
83ff19ec |
992 | if ((a & 0xfc00) != 0x5000) { |
993 | PicoWrite8_io(a, d); |
994 | return; |
995 | } |
974fdb5b |
996 | |
5609d343 |
997 | if (!(Pico32x.regs[0] & P32XS_FM)) { |
998 | if ((a & 0xfff0) == 0x5180) { // a15180 |
999 | p32x_vdp_write8(a, d); |
1000 | return; |
1001 | } |
be2c4208 |
1002 | |
5609d343 |
1003 | // TODO: verify |
1004 | if ((a & 0xfe00) == 0x5200) { // a15200 |
1005 | elprintf(EL_32X|EL_ANOMALY, "m68k 32x PAL w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
1006 | ((u8 *)Pico32xMem->pal)[(a & 0x1ff) ^ 1] = d; |
1007 | Pico32x.dirty_pal = 1; |
1008 | return; |
1009 | } |
974fdb5b |
1010 | } |
1011 | |
be2c4208 |
1012 | elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
1013 | } |
1014 | |
83ff19ec |
1015 | static void PicoWrite16_32x_on(u32 a, u32 d) |
be2c4208 |
1016 | { |
1017 | if ((a & 0xfc00) == 0x5000) |
553c3eaa |
1018 | elprintf(EL_32X, "m68k 32x w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc); |
be2c4208 |
1019 | |
1020 | if ((a & 0xffc0) == 0x5100) { // a15100 |
1021 | p32x_reg_write16(a, d); |
1022 | return; |
1023 | } |
1024 | |
83ff19ec |
1025 | if ((a & 0xfc00) != 0x5000) { |
1026 | PicoWrite16_io(a, d); |
1027 | return; |
1028 | } |
974fdb5b |
1029 | |
5609d343 |
1030 | if (!(Pico32x.regs[0] & P32XS_FM)) { |
1031 | if ((a & 0xfff0) == 0x5180) { // a15180 |
1032 | p32x_vdp_write16(a, d, NULL); // FIXME? |
1033 | return; |
1034 | } |
be2c4208 |
1035 | |
5609d343 |
1036 | if ((a & 0xfe00) == 0x5200) { // a15200 |
1037 | Pico32xMem->pal[(a & 0x1ff) / 2] = d; |
1038 | Pico32x.dirty_pal = 1; |
1039 | return; |
1040 | } |
974fdb5b |
1041 | } |
1042 | |
be2c4208 |
1043 | elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc); |
1044 | } |
1045 | |
83ff19ec |
1046 | // before ADEN |
1047 | u32 PicoRead8_32x(u32 a) |
1048 | { |
1049 | u32 d = 0; |
1050 | if ((a & 0xffc0) == 0x5100) { // a15100 |
1051 | // regs are always readable |
1052 | d = ((u8 *)Pico32x.regs)[(a & 0x3f) ^ 1]; |
1053 | goto out; |
1054 | } |
1055 | |
1056 | if ((a & 0xfffc) == 0x30ec) { // a130ec |
1057 | d = str_mars[a & 3]; |
1058 | goto out; |
1059 | } |
1060 | |
1061 | elprintf(EL_UIO, "m68k unmapped r8 [%06x] @%06x", a, SekPc); |
1062 | return d; |
1063 | |
1064 | out: |
1065 | elprintf(EL_32X, "m68k 32x r8 [%06x] %02x @%06x", a, d, SekPc); |
1066 | return d; |
1067 | } |
1068 | |
1069 | u32 PicoRead16_32x(u32 a) |
1070 | { |
1071 | u32 d = 0; |
1072 | if ((a & 0xffc0) == 0x5100) { // a15100 |
1073 | d = Pico32x.regs[(a & 0x3f) / 2]; |
1074 | goto out; |
1075 | } |
1076 | |
1077 | if ((a & 0xfffc) == 0x30ec) { // a130ec |
1078 | d = !(a & 2) ? ('M'<<8)|'A' : ('R'<<8)|'S'; |
1079 | goto out; |
1080 | } |
1081 | |
1082 | elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc); |
1083 | return d; |
1084 | |
1085 | out: |
1086 | elprintf(EL_32X, "m68k 32x r16 [%06x] %04x @%06x", a, d, SekPc); |
1087 | return d; |
1088 | } |
1089 | |
1090 | void PicoWrite8_32x(u32 a, u32 d) |
1091 | { |
1092 | if ((a & 0xffc0) == 0x5100) { // a15100 |
1093 | u16 *r = Pico32x.regs; |
1094 | |
1095 | elprintf(EL_32X, "m68k 32x w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
1096 | a &= 0x3f; |
1097 | if (a == 1) { |
1098 | if ((d ^ r[0]) & d & P32XS_ADEN) { |
1099 | Pico32xStartup(); |
1100 | r[0] &= ~P32XS_nRES; // causes reset if specified by this write |
1101 | r[0] |= P32XS_ADEN; |
1102 | p32x_reg_write8(a, d); // forward for reset processing |
1103 | } |
1104 | return; |
1105 | } |
1106 | |
1107 | // allow only COMM for now |
1108 | if ((a & 0x30) == 0x20) { |
1109 | u8 *r8 = (u8 *)r; |
1110 | r8[a ^ 1] = d; |
1111 | } |
1112 | return; |
1113 | } |
1114 | |
1115 | elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
1116 | } |
1117 | |
1118 | void PicoWrite16_32x(u32 a, u32 d) |
1119 | { |
1120 | if ((a & 0xffc0) == 0x5100) { // a15100 |
1121 | u16 *r = Pico32x.regs; |
1122 | |
1123 | elprintf(EL_UIO, "m68k 32x w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc); |
1124 | a &= 0x3e; |
1125 | if (a == 0) { |
1126 | if ((d ^ r[0]) & d & P32XS_ADEN) { |
1127 | Pico32xStartup(); |
1128 | r[0] &= ~P32XS_nRES; // causes reset if specified by this write |
1129 | r[0] |= P32XS_ADEN; |
1130 | p32x_reg_write16(a, d); // forward for reset processing |
1131 | } |
1132 | return; |
1133 | } |
1134 | |
1135 | // allow only COMM for now |
1136 | if ((a & 0x30) == 0x20) |
1137 | r[a / 2] = d; |
1138 | return; |
1139 | } |
1140 | |
1141 | elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc); |
1142 | } |
1143 | |
1144 | // ----------------------------------------------------------------- |
1145 | |
be2c4208 |
1146 | // hint vector is writeable |
1147 | static void PicoWrite8_hint(u32 a, u32 d) |
1148 | { |
1149 | if ((a & 0xfffc) == 0x0070) { |
1150 | Pico32xMem->m68k_rom[a ^ 1] = d; |
1151 | return; |
1152 | } |
1153 | |
1154 | elprintf(EL_UIO, "m68k unmapped w8 [%06x] %02x @%06x", a, d & 0xff, SekPc); |
1155 | } |
1156 | |
1157 | static void PicoWrite16_hint(u32 a, u32 d) |
1158 | { |
1159 | if ((a & 0xfffc) == 0x0070) { |
1160 | ((u16 *)Pico32xMem->m68k_rom)[a/2] = d; |
1161 | return; |
1162 | } |
1163 | |
1164 | elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc); |
1165 | } |
1166 | |
5e49c3a8 |
1167 | static void bank_switch(int b) |
1168 | { |
1169 | unsigned int rs, bank; |
1170 | |
1171 | bank = b << 20; |
1172 | if (bank >= Pico.romsize) { |
1173 | elprintf(EL_32X|EL_ANOMALY, "missing bank @ %06x", bank); |
1174 | return; |
1175 | } |
1176 | |
1177 | // 32X ROM (unbanked, XXX: consider mirroring?) |
1178 | rs = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK; |
1179 | rs -= bank; |
1180 | if (rs > 0x100000) |
1181 | rs = 0x100000; |
1182 | cpu68k_map_set(m68k_read8_map, 0x900000, 0x900000 + rs - 1, Pico.rom + bank, 0); |
1183 | cpu68k_map_set(m68k_read16_map, 0x900000, 0x900000 + rs - 1, Pico.rom + bank, 0); |
1184 | |
1185 | elprintf(EL_32X, "bank %06x-%06x -> %06x", 0x900000, 0x900000 + rs - 1, bank); |
602c28ca |
1186 | |
1187 | #ifdef EMU_F68K |
1188 | // setup FAME fetchmap |
1189 | for (rs = 0x90; rs < 0xa0; rs++) |
be26eb23 |
1190 | PicoCpuFM68k.Fetch[rs] = (unsigned long)Pico.rom + bank - 0x900000; |
602c28ca |
1191 | #endif |
5e49c3a8 |
1192 | } |
1193 | |
acd35d4c |
1194 | // ----------------------------------------------------------------- |
1195 | // SH2 |
1196 | // ----------------------------------------------------------------- |
1197 | |
bcf65fd6 |
1198 | // read8 |
1199 | static u32 sh2_read8_unmapped(u32 a, int id) |
acd35d4c |
1200 | { |
bcf65fd6 |
1201 | elprintf(EL_UIO, "%csh2 unmapped r8 [%08x] %02x @%06x", |
1202 | id ? 's' : 'm', a, 0, sh2_pc(id)); |
1203 | return 0; |
1204 | } |
b78efee2 |
1205 | |
bcf65fd6 |
1206 | static u32 sh2_read8_cs0(u32 a, int id) |
1207 | { |
1208 | u32 d = 0; |
97d3f47f |
1209 | |
bcf65fd6 |
1210 | // 0x3ff00 is veridied |
1211 | if ((a & 0x3ff00) == 0x4000) { |
b78efee2 |
1212 | d = p32x_sh2reg_read16(a, id); |
db1d3564 |
1213 | goto out_16to8; |
acd35d4c |
1214 | } |
1215 | |
bcf65fd6 |
1216 | if ((a & 0x3ff00) == 0x4100) { |
acd35d4c |
1217 | d = p32x_vdp_read16(a); |
19886062 |
1218 | sh2_poll_detect(&sh2s[id], a, SH2_STATE_VPOLL); |
db1d3564 |
1219 | goto out_16to8; |
acd35d4c |
1220 | } |
1221 | |
bcf65fd6 |
1222 | // TODO: mirroring? |
1223 | if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m)) |
1224 | return Pico32xMem->sh2_rom_m[a ^ 1]; |
1225 | if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s)) |
1226 | return Pico32xMem->sh2_rom_s[a ^ 1]; |
1227 | |
1f1ff763 |
1228 | if ((a & 0x3fe00) == 0x4200) { |
acd35d4c |
1229 | d = Pico32xMem->pal[(a & 0x1ff) / 2]; |
1230 | goto out_16to8; |
1231 | } |
1232 | |
bcf65fd6 |
1233 | return sh2_read8_unmapped(a, id); |
acd35d4c |
1234 | |
1235 | out_16to8: |
1236 | if (a & 1) |
1237 | d &= 0xff; |
1238 | else |
1239 | d >>= 8; |
1240 | |
b78efee2 |
1241 | elprintf(EL_32X, "%csh2 r8 [%08x] %02x @%06x", |
1242 | id ? 's' : 'm', a, d, sh2_pc(id)); |
acd35d4c |
1243 | return d; |
1244 | } |
1245 | |
bcf65fd6 |
1246 | static u32 sh2_read8_da(u32 a, int id) |
acd35d4c |
1247 | { |
bcf65fd6 |
1248 | return Pico32xMem->data_array[id][(a & 0xfff) ^ 1]; |
1249 | } |
acd35d4c |
1250 | |
bcf65fd6 |
1251 | // read16 |
1252 | static u32 sh2_read16_unmapped(u32 a, int id) |
1253 | { |
1254 | elprintf(EL_UIO, "%csh2 unmapped r16 [%08x] %04x @%06x", |
1255 | id ? 's' : 'm', a, 0, sh2_pc(id)); |
1256 | return 0; |
1257 | } |
b78efee2 |
1258 | |
bcf65fd6 |
1259 | static u32 sh2_read16_cs0(u32 a, int id) |
1260 | { |
1261 | u32 d = 0; |
97d3f47f |
1262 | |
bcf65fd6 |
1263 | if ((a & 0x3ff00) == 0x4000) { |
b78efee2 |
1264 | d = p32x_sh2reg_read16(a, id); |
1b3f5844 |
1265 | if (!(EL_LOGMASK & EL_PWM) && (a & 0x30) == 0x30) // hide PWM |
1266 | return d; |
db1d3564 |
1267 | goto out; |
acd35d4c |
1268 | } |
1269 | |
bcf65fd6 |
1270 | if ((a & 0x3ff00) == 0x4100) { |
acd35d4c |
1271 | d = p32x_vdp_read16(a); |
19886062 |
1272 | sh2_poll_detect(&sh2s[id], a, SH2_STATE_VPOLL); |
db1d3564 |
1273 | goto out; |
acd35d4c |
1274 | } |
1275 | |
bcf65fd6 |
1276 | if (id == 0 && a < sizeof(Pico32xMem->sh2_rom_m)) |
1277 | return *(u16 *)(Pico32xMem->sh2_rom_m + a); |
1278 | if (id == 1 && a < sizeof(Pico32xMem->sh2_rom_s)) |
1279 | return *(u16 *)(Pico32xMem->sh2_rom_s + a); |
1280 | |
1f1ff763 |
1281 | if ((a & 0x3fe00) == 0x4200) { |
acd35d4c |
1282 | d = Pico32xMem->pal[(a & 0x1ff) / 2]; |
1283 | goto out; |
1284 | } |
1285 | |
bcf65fd6 |
1286 | return sh2_read16_unmapped(a, id); |
acd35d4c |
1287 | |
1288 | out: |
b78efee2 |
1289 | elprintf(EL_32X, "%csh2 r16 [%08x] %04x @%06x", |
1290 | id ? 's' : 'm', a, d, sh2_pc(id)); |
acd35d4c |
1291 | return d; |
1292 | } |
1293 | |
bcf65fd6 |
1294 | static u32 sh2_read16_da(u32 a, int id) |
acd35d4c |
1295 | { |
bcf65fd6 |
1296 | return ((u16 *)Pico32xMem->data_array[id])[(a & 0xfff) / 2]; |
acd35d4c |
1297 | } |
1298 | |
e05b81fc |
1299 | static int REGPARM(3) sh2_write_ignore(u32 a, u32 d, int id) |
4b315c21 |
1300 | { |
e05b81fc |
1301 | return 0; |
4b315c21 |
1302 | } |
1303 | |
bcf65fd6 |
1304 | // write8 |
e05b81fc |
1305 | static int REGPARM(3) sh2_write8_unmapped(u32 a, u32 d, int id) |
acd35d4c |
1306 | { |
bcf65fd6 |
1307 | elprintf(EL_UIO, "%csh2 unmapped w8 [%08x] %02x @%06x", |
1308 | id ? 's' : 'm', a, d & 0xff, sh2_pc(id)); |
e05b81fc |
1309 | return 0; |
bcf65fd6 |
1310 | } |
266c6afa |
1311 | |
e05b81fc |
1312 | static int REGPARM(3) sh2_write8_cs0(u32 a, u32 d, int id) |
bcf65fd6 |
1313 | { |
1314 | elprintf(EL_32X, "%csh2 w8 [%08x] %02x @%06x", |
1315 | id ? 's' : 'm', a, d & 0xff, sh2_pc(id)); |
b78efee2 |
1316 | |
5609d343 |
1317 | if (Pico32x.regs[0] & P32XS_FM) { |
1318 | if ((a & 0x3ff00) == 0x4100) { |
1319 | sh2s[id].poll_addr = 0; |
1320 | p32x_vdp_write8(a, d); |
1321 | return 0; |
1322 | } |
acd35d4c |
1323 | } |
1324 | |
bcf65fd6 |
1325 | if ((a & 0x3ff00) == 0x4000) { |
b78efee2 |
1326 | p32x_sh2reg_write8(a, d, id); |
e05b81fc |
1327 | return 1; |
acd35d4c |
1328 | } |
1329 | |
e05b81fc |
1330 | return sh2_write8_unmapped(a, d, id); |
bcf65fd6 |
1331 | } |
1332 | |
e51e5983 |
1333 | /* quirk: in both normal and overwrite areas only nonzero values go through */ |
bcf65fd6 |
1334 | #define sh2_write8_dramN(n) \ |
e51e5983 |
1335 | if ((d & 0xff) != 0) { \ |
bcf65fd6 |
1336 | u8 *dram = (u8 *)Pico32xMem->dram[n]; \ |
1337 | dram[(a & 0x1ffff) ^ 1] = d; \ |
e05b81fc |
1338 | } \ |
1339 | return 0; |
87accdf7 |
1340 | |
e05b81fc |
1341 | static int REGPARM(3) sh2_write8_dram0(u32 a, u32 d, int id) |
bcf65fd6 |
1342 | { |
1343 | sh2_write8_dramN(0); |
acd35d4c |
1344 | } |
1345 | |
e05b81fc |
1346 | static int REGPARM(3) sh2_write8_dram1(u32 a, u32 d, int id) |
acd35d4c |
1347 | { |
bcf65fd6 |
1348 | sh2_write8_dramN(1); |
1349 | } |
87accdf7 |
1350 | |
e05b81fc |
1351 | static int REGPARM(3) sh2_write8_sdram(u32 a, u32 d, int id) |
f4bb5d6b |
1352 | { |
1353 | u32 a1 = a & 0x3ffff; |
1354 | #ifdef DRC_SH2 |
1355 | int t = Pico32xMem->drcblk_ram[a1 >> SH2_DRCBLK_RAM_SHIFT]; |
1356 | if (t) |
1357 | sh2_drc_wcheck_ram(a, t, id); |
1358 | #endif |
1359 | Pico32xMem->sdram[a1 ^ 1] = d; |
e05b81fc |
1360 | return 0; |
f4bb5d6b |
1361 | } |
1362 | |
e05b81fc |
1363 | static int REGPARM(3) sh2_write8_da(u32 a, u32 d, int id) |
bcf65fd6 |
1364 | { |
f4bb5d6b |
1365 | u32 a1 = a & 0xfff; |
1366 | #ifdef DRC_SH2 |
1367 | int t = Pico32xMem->drcblk_da[id][a1 >> SH2_DRCBLK_DA_SHIFT]; |
1368 | if (t) |
1369 | sh2_drc_wcheck_da(a, t, id); |
1370 | #endif |
1371 | Pico32xMem->data_array[id][a1 ^ 1] = d; |
e05b81fc |
1372 | return 0; |
bcf65fd6 |
1373 | } |
acd35d4c |
1374 | |
bcf65fd6 |
1375 | // write16 |
e05b81fc |
1376 | static int REGPARM(3) sh2_write16_unmapped(u32 a, u32 d, int id) |
bcf65fd6 |
1377 | { |
1378 | elprintf(EL_UIO, "%csh2 unmapped w16 [%08x] %04x @%06x", |
1379 | id ? 's' : 'm', a, d & 0xffff, sh2_pc(id)); |
e05b81fc |
1380 | return 0; |
bcf65fd6 |
1381 | } |
b78efee2 |
1382 | |
e05b81fc |
1383 | static int REGPARM(3) sh2_write16_cs0(u32 a, u32 d, int id) |
bcf65fd6 |
1384 | { |
1385 | if (((EL_LOGMASK & EL_PWM) || (a & 0x30) != 0x30)) // hide PWM |
1386 | elprintf(EL_32X, "%csh2 w16 [%08x] %04x @%06x", |
1387 | id ? 's' : 'm', a, d & 0xffff, sh2_pc(id)); |
266c6afa |
1388 | |
5609d343 |
1389 | if (Pico32x.regs[0] & P32XS_FM) { |
1390 | if ((a & 0x3ff00) == 0x4100) { |
1391 | sh2s[id].poll_addr = 0; |
1392 | p32x_vdp_write16(a, d, &sh2s[id]); |
1393 | return 0; |
1394 | } |
acd35d4c |
1395 | |
5609d343 |
1396 | if ((a & 0x3fe00) == 0x4200) { |
1397 | Pico32xMem->pal[(a & 0x1ff) / 2] = d; |
1398 | Pico32x.dirty_pal = 1; |
1399 | return 0; |
1400 | } |
acd35d4c |
1401 | } |
1402 | |
bcf65fd6 |
1403 | if ((a & 0x3ff00) == 0x4000) { |
b78efee2 |
1404 | p32x_sh2reg_write16(a, d, id); |
e05b81fc |
1405 | return 1; |
acd35d4c |
1406 | } |
1407 | |
e05b81fc |
1408 | return sh2_write16_unmapped(a, d, id); |
bcf65fd6 |
1409 | } |
1410 | |
1411 | #define sh2_write16_dramN(n) \ |
1412 | u16 *pd = &Pico32xMem->dram[n][(a & 0x1ffff) / 2]; \ |
1413 | if (!(a & 0x20000)) { \ |
1414 | *pd = d; \ |
e05b81fc |
1415 | return 0; \ |
bcf65fd6 |
1416 | } \ |
1417 | /* overwrite */ \ |
1418 | if (!(d & 0xff00)) d |= *pd & 0xff00; \ |
1419 | if (!(d & 0x00ff)) d |= *pd & 0x00ff; \ |
e05b81fc |
1420 | *pd = d; \ |
1421 | return 0 |
bcf65fd6 |
1422 | |
e05b81fc |
1423 | static int REGPARM(3) sh2_write16_dram0(u32 a, u32 d, int id) |
bcf65fd6 |
1424 | { |
1425 | sh2_write16_dramN(0); |
1426 | } |
1427 | |
e05b81fc |
1428 | static int REGPARM(3) sh2_write16_dram1(u32 a, u32 d, int id) |
bcf65fd6 |
1429 | { |
1430 | sh2_write16_dramN(1); |
1431 | } |
1432 | |
e05b81fc |
1433 | static int REGPARM(3) sh2_write16_sdram(u32 a, u32 d, int id) |
f4bb5d6b |
1434 | { |
1435 | u32 a1 = a & 0x3ffff; |
1436 | #ifdef DRC_SH2 |
1437 | int t = Pico32xMem->drcblk_ram[a1 >> SH2_DRCBLK_RAM_SHIFT]; |
1438 | if (t) |
1439 | sh2_drc_wcheck_ram(a, t, id); |
1440 | #endif |
1441 | ((u16 *)Pico32xMem->sdram)[a1 / 2] = d; |
e05b81fc |
1442 | return 0; |
f4bb5d6b |
1443 | } |
1444 | |
e05b81fc |
1445 | static int REGPARM(3) sh2_write16_da(u32 a, u32 d, int id) |
bcf65fd6 |
1446 | { |
f4bb5d6b |
1447 | u32 a1 = a & 0xfff; |
1448 | #ifdef DRC_SH2 |
1449 | int t = Pico32xMem->drcblk_da[id][a1 >> SH2_DRCBLK_DA_SHIFT]; |
1450 | if (t) |
1451 | sh2_drc_wcheck_da(a, t, id); |
1452 | #endif |
1453 | ((u16 *)Pico32xMem->data_array[id])[a1 / 2] = d; |
e05b81fc |
1454 | return 0; |
bcf65fd6 |
1455 | } |
1456 | |
1457 | |
e05b81fc |
1458 | typedef u32 (sh2_read_handler)(u32 a, int id); |
1459 | typedef int REGPARM(3) (sh2_write_handler)(u32 a, u32 d, int id); |
bcf65fd6 |
1460 | |
e05b81fc |
1461 | #define SH2MAP_ADDR2OFFS_R(a) \ |
1462 | ((((a) >> 25) & 3) | (((a) >> 27) & 0x1c)) |
1463 | |
1464 | #define SH2MAP_ADDR2OFFS_W(a) \ |
1465 | ((u32)(a) >> SH2_WRITE_SHIFT) |
bcf65fd6 |
1466 | |
80599a42 |
1467 | u32 REGPARM(2) p32x_sh2_read8(u32 a, SH2 *sh2) |
bcf65fd6 |
1468 | { |
1469 | const sh2_memmap *sh2_map = sh2->read8_map; |
1470 | uptr p; |
1471 | |
e05b81fc |
1472 | sh2_map += SH2MAP_ADDR2OFFS_R(a); |
bcf65fd6 |
1473 | p = sh2_map->addr; |
b8a1c09a |
1474 | if (map_flag_set(p)) |
bcf65fd6 |
1475 | return ((sh2_read_handler *)(p << 1))(a, sh2->is_slave); |
1476 | else |
1477 | return *(u8 *)((p << 1) + ((a & sh2_map->mask) ^ 1)); |
1478 | } |
1479 | |
80599a42 |
1480 | u32 REGPARM(2) p32x_sh2_read16(u32 a, SH2 *sh2) |
bcf65fd6 |
1481 | { |
1482 | const sh2_memmap *sh2_map = sh2->read16_map; |
1483 | uptr p; |
1484 | |
e05b81fc |
1485 | sh2_map += SH2MAP_ADDR2OFFS_R(a); |
bcf65fd6 |
1486 | p = sh2_map->addr; |
b8a1c09a |
1487 | if (map_flag_set(p)) |
bcf65fd6 |
1488 | return ((sh2_read_handler *)(p << 1))(a, sh2->is_slave); |
1489 | else |
1490 | return *(u16 *)((p << 1) + ((a & sh2_map->mask) & ~1)); |
1491 | } |
1492 | |
80599a42 |
1493 | u32 REGPARM(2) p32x_sh2_read32(u32 a, SH2 *sh2) |
bcf65fd6 |
1494 | { |
1495 | const sh2_memmap *sh2_map = sh2->read16_map; |
1496 | sh2_read_handler *handler; |
1497 | u32 offs; |
1498 | uptr p; |
1499 | |
e05b81fc |
1500 | offs = SH2MAP_ADDR2OFFS_R(a); |
bcf65fd6 |
1501 | sh2_map += offs; |
1502 | p = sh2_map->addr; |
b8a1c09a |
1503 | if (!map_flag_set(p)) { |
bcf65fd6 |
1504 | // XXX: maybe 32bit access instead with ror? |
1505 | u16 *pd = (u16 *)((p << 1) + ((a & sh2_map->mask) & ~1)); |
1506 | return (pd[0] << 16) | pd[1]; |
1d7a28a7 |
1507 | } |
1508 | |
bcf65fd6 |
1509 | if (offs == 0x1f) |
1510 | return sh2_peripheral_read32(a, sh2->is_slave); |
1511 | |
1512 | handler = (sh2_read_handler *)(p << 1); |
1513 | return (handler(a, sh2->is_slave) << 16) | handler(a + 2, sh2->is_slave); |
1514 | } |
1515 | |
e05b81fc |
1516 | // return nonzero if write potentially causes an interrupt (used by drc) |
1517 | int REGPARM(3) p32x_sh2_write8(u32 a, u32 d, SH2 *sh2) |
bcf65fd6 |
1518 | { |
f4bb5d6b |
1519 | const void **sh2_wmap = sh2->write8_tab; |
1520 | sh2_write_handler *wh; |
bcf65fd6 |
1521 | |
e05b81fc |
1522 | wh = sh2_wmap[SH2MAP_ADDR2OFFS_W(a)]; |
1523 | return wh(a, d, sh2->is_slave); |
bcf65fd6 |
1524 | } |
1525 | |
e05b81fc |
1526 | int REGPARM(3) p32x_sh2_write16(u32 a, u32 d, SH2 *sh2) |
bcf65fd6 |
1527 | { |
f4bb5d6b |
1528 | const void **sh2_wmap = sh2->write16_tab; |
1529 | sh2_write_handler *wh; |
bcf65fd6 |
1530 | |
e05b81fc |
1531 | wh = sh2_wmap[SH2MAP_ADDR2OFFS_W(a)]; |
1532 | return wh(a, d, sh2->is_slave); |
acd35d4c |
1533 | } |
1534 | |
e05b81fc |
1535 | int REGPARM(3) p32x_sh2_write32(u32 a, u32 d, SH2 *sh2) |
acd35d4c |
1536 | { |
f4bb5d6b |
1537 | const void **sh2_wmap = sh2->write16_tab; |
bcf65fd6 |
1538 | sh2_write_handler *handler; |
1539 | u32 offs; |
bcf65fd6 |
1540 | |
e05b81fc |
1541 | offs = SH2MAP_ADDR2OFFS_W(a); |
bcf65fd6 |
1542 | |
e05b81fc |
1543 | if (offs == SH2MAP_ADDR2OFFS_W(0xffffc000)) { |
bcf65fd6 |
1544 | sh2_peripheral_write32(a, d, sh2->is_slave); |
e05b81fc |
1545 | return 0; |
4ea707e1 |
1546 | } |
1547 | |
f4bb5d6b |
1548 | handler = sh2_wmap[offs]; |
bcf65fd6 |
1549 | handler(a, d >> 16, sh2->is_slave); |
1550 | handler(a + 2, d, sh2->is_slave); |
e05b81fc |
1551 | return 0; |
acd35d4c |
1552 | } |
1553 | |
bcf65fd6 |
1554 | // ----------------------------------------------------------------- |
1555 | |
83ff19ec |
1556 | static const u16 msh2_code[] = { |
1557 | // trap instructions |
1558 | 0xaffe, // bra <self> |
1559 | 0x0009, // nop |
1560 | // have to wait a bit until m68k initial program finishes clearing stuff |
1561 | // to avoid races with game SH2 code, like in Tempo |
1562 | 0xd004, // mov.l @(_m_ok,pc), r0 |
1563 | 0xd105, // mov.l @(_cnt,pc), r1 |
1564 | 0xd205, // mov.l @(_start,pc), r2 |
1565 | 0x71ff, // add #-1, r1 |
1566 | 0x4115, // cmp/pl r1 |
1567 | 0x89fc, // bt -2 |
1568 | 0xc208, // mov.l r0, @(h'20,gbr) |
1569 | 0x6822, // mov.l @r2, r8 |
1570 | 0x482b, // jmp @r8 |
1571 | 0x0009, // nop |
1572 | ('M'<<8)|'_', ('O'<<8)|'K', |
1573 | 0x0001, 0x0000, |
1574 | 0x2200, 0x03e0 // master start pointer in ROM |
1575 | }; |
1576 | |
1577 | static const u16 ssh2_code[] = { |
1578 | 0xaffe, // bra <self> |
1579 | 0x0009, // nop |
1580 | // code to wait for master, in case authentic master BIOS is used |
1581 | 0xd104, // mov.l @(_m_ok,pc), r1 |
1582 | 0xd206, // mov.l @(_start,pc), r2 |
1583 | 0xc608, // mov.l @(h'20,gbr), r0 |
1584 | 0x3100, // cmp/eq r0, r1 |
1585 | 0x8bfc, // bf #-2 |
1586 | 0xd003, // mov.l @(_s_ok,pc), r0 |
1587 | 0xc209, // mov.l r0, @(h'24,gbr) |
1588 | 0x6822, // mov.l @r2, r8 |
1589 | 0x482b, // jmp @r8 |
1590 | 0x0009, // nop |
1591 | ('M'<<8)|'_', ('O'<<8)|'K', |
1592 | ('S'<<8)|'_', ('O'<<8)|'K', |
1593 | 0x2200, 0x03e4 // slave start pointer in ROM |
1594 | }; |
1595 | |
da77daa9 |
1596 | #define HWSWAP(x) (((u16)(x) << 16) | ((x) >> 16)) |
83ff19ec |
1597 | static void get_bios(void) |
be2c4208 |
1598 | { |
83ff19ec |
1599 | u16 *ps; |
1600 | u32 *pl; |
be2c4208 |
1601 | int i; |
1602 | |
83ff19ec |
1603 | // M68K ROM |
1604 | if (p32x_bios_g != NULL) { |
1605 | elprintf(EL_STATUS|EL_32X, "32x: using supplied 68k BIOS"); |
b4db550e |
1606 | Byteswap(Pico32xMem->m68k_rom, p32x_bios_g, sizeof(Pico32xMem->m68k_rom)); |
be2c4208 |
1607 | } |
83ff19ec |
1608 | else { |
1609 | // generate 68k ROM |
1610 | ps = (u16 *)Pico32xMem->m68k_rom; |
1611 | pl = (u32 *)ps; |
1612 | for (i = 1; i < 0xc0/4; i++) |
1613 | pl[i] = HWSWAP(0x880200 + (i - 1) * 6); |
be2c4208 |
1614 | |
83ff19ec |
1615 | // fill with nops |
1616 | for (i = 0xc0/2; i < 0x100/2; i++) |
1617 | ps[i] = 0x4e71; |
be2c4208 |
1618 | |
5e49c3a8 |
1619 | #if 0 |
83ff19ec |
1620 | ps[0xc0/2] = 0x46fc; |
1621 | ps[0xc2/2] = 0x2700; // move #0x2700,sr |
1622 | ps[0xfe/2] = 0x60fe; // jump to self |
5e49c3a8 |
1623 | #else |
83ff19ec |
1624 | ps[0xfe/2] = 0x4e75; // rts |
5e49c3a8 |
1625 | #endif |
83ff19ec |
1626 | } |
1627 | // fill remaining m68k_rom page with game ROM |
b4db550e |
1628 | memcpy(Pico32xMem->m68k_rom_bank + sizeof(Pico32xMem->m68k_rom), |
1629 | Pico.rom + sizeof(Pico32xMem->m68k_rom), |
1630 | sizeof(Pico32xMem->m68k_rom_bank) - sizeof(Pico32xMem->m68k_rom)); |
be2c4208 |
1631 | |
83ff19ec |
1632 | // MSH2 |
1633 | if (p32x_bios_m != NULL) { |
1634 | elprintf(EL_STATUS|EL_32X, "32x: using supplied master SH2 BIOS"); |
1635 | Byteswap(Pico32xMem->sh2_rom_m, p32x_bios_m, sizeof(Pico32xMem->sh2_rom_m)); |
acd35d4c |
1636 | } |
83ff19ec |
1637 | else { |
1638 | pl = (u32 *)Pico32xMem->sh2_rom_m; |
1639 | |
1640 | // fill exception vector table to our trap address |
1641 | for (i = 0; i < 128; i++) |
1642 | pl[i] = HWSWAP(0x200); |
1643 | |
1644 | // startup code |
1645 | memcpy(Pico32xMem->sh2_rom_m + 0x200, msh2_code, sizeof(msh2_code)); |
1646 | |
1647 | // reset SP |
1648 | pl[1] = pl[3] = HWSWAP(0x6040000); |
1649 | // start |
1650 | pl[0] = pl[2] = HWSWAP(0x204); |
1651 | } |
1652 | |
1653 | // SSH2 |
1654 | if (p32x_bios_s != NULL) { |
1655 | elprintf(EL_STATUS|EL_32X, "32x: using supplied slave SH2 BIOS"); |
1656 | Byteswap(Pico32xMem->sh2_rom_s, p32x_bios_s, sizeof(Pico32xMem->sh2_rom_s)); |
1657 | } |
1658 | else { |
1659 | pl = (u32 *)Pico32xMem->sh2_rom_s; |
1660 | |
1661 | // fill exception vector table to our trap address |
1662 | for (i = 0; i < 128; i++) |
1663 | pl[i] = HWSWAP(0x200); |
1664 | |
1665 | // startup code |
1666 | memcpy(Pico32xMem->sh2_rom_s + 0x200, ssh2_code, sizeof(ssh2_code)); |
1667 | |
1668 | // reset SP |
1669 | pl[1] = pl[3] = HWSWAP(0x603f800); |
1670 | // start |
1671 | pl[0] = pl[2] = HWSWAP(0x204); |
1672 | } |
1673 | } |
1674 | |
bcf65fd6 |
1675 | #define MAP_MEMORY(m) ((uptr)(m) >> 1) |
b8a1c09a |
1676 | #define MAP_HANDLER(h) ( ((uptr)(h) >> 1) | ((uptr)1 << (sizeof(uptr) * 8 - 1)) ) |
bcf65fd6 |
1677 | |
1678 | static sh2_memmap sh2_read8_map[0x20], sh2_read16_map[0x20]; |
f4bb5d6b |
1679 | // for writes we are using handlers only |
e05b81fc |
1680 | static sh2_write_handler *sh2_write8_map[0x80], *sh2_write16_map[0x80]; |
bcf65fd6 |
1681 | |
1682 | void Pico32xSwapDRAM(int b) |
1683 | { |
1684 | cpu68k_map_set(m68k_read8_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0); |
1685 | cpu68k_map_set(m68k_read16_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0); |
1686 | cpu68k_map_set(m68k_write8_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0); |
1687 | cpu68k_map_set(m68k_write16_map, 0x840000, 0x85ffff, Pico32xMem->dram[b], 0); |
1688 | |
1689 | // SH2 |
1690 | sh2_read8_map[2].addr = sh2_read8_map[6].addr = |
1691 | sh2_read16_map[2].addr = sh2_read16_map[6].addr = MAP_MEMORY(Pico32xMem->dram[b]); |
1692 | |
e05b81fc |
1693 | sh2_write8_map[0x04/2] = sh2_write8_map[0x24/2] = b ? sh2_write8_dram1 : sh2_write8_dram0; |
1694 | sh2_write16_map[0x04/2] = sh2_write16_map[0x24/2] = b ? sh2_write16_dram1 : sh2_write16_dram0; |
bcf65fd6 |
1695 | } |
1696 | |
83ff19ec |
1697 | void PicoMemSetup32x(void) |
1698 | { |
1699 | unsigned int rs; |
bcf65fd6 |
1700 | int i; |
83ff19ec |
1701 | |
e743be20 |
1702 | Pico32xMem = plat_mmap(0x06000000, sizeof(*Pico32xMem), 0, 0); |
83ff19ec |
1703 | if (Pico32xMem == NULL) { |
1704 | elprintf(EL_STATUS, "OOM"); |
1705 | return; |
1706 | } |
1707 | |
83ff19ec |
1708 | get_bios(); |
acd35d4c |
1709 | |
be2c4208 |
1710 | // cartridge area becomes unmapped |
1711 | // XXX: we take the easy way and don't unmap ROM, |
1712 | // so that we can avoid handling the RV bit. |
1713 | // m68k_map_unmap(0x000000, 0x3fffff); |
1714 | |
1715 | // MD ROM area |
b4db550e |
1716 | rs = sizeof(Pico32xMem->m68k_rom_bank); |
1717 | cpu68k_map_set(m68k_read8_map, 0x000000, rs - 1, Pico32xMem->m68k_rom_bank, 0); |
1718 | cpu68k_map_set(m68k_read16_map, 0x000000, rs - 1, Pico32xMem->m68k_rom_bank, 0); |
974fdb5b |
1719 | cpu68k_map_set(m68k_write8_map, 0x000000, rs - 1, PicoWrite8_hint, 1); // TODO verify |
1720 | cpu68k_map_set(m68k_write16_map, 0x000000, rs - 1, PicoWrite16_hint, 1); |
1721 | |
be2c4208 |
1722 | // 32X ROM (unbanked, XXX: consider mirroring?) |
5e49c3a8 |
1723 | rs = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK; |
1724 | if (rs > 0x80000) |
1725 | rs = 0x80000; |
1726 | cpu68k_map_set(m68k_read8_map, 0x880000, 0x880000 + rs - 1, Pico.rom, 0); |
1727 | cpu68k_map_set(m68k_read16_map, 0x880000, 0x880000 + rs - 1, Pico.rom, 0); |
602c28ca |
1728 | #ifdef EMU_F68K |
1729 | // setup FAME fetchmap |
be26eb23 |
1730 | PicoCpuFM68k.Fetch[0] = (unsigned long)Pico32xMem->m68k_rom; |
602c28ca |
1731 | for (rs = 0x88; rs < 0x90; rs++) |
be26eb23 |
1732 | PicoCpuFM68k.Fetch[rs] = (unsigned long)Pico.rom - 0x880000; |
602c28ca |
1733 | #endif |
be2c4208 |
1734 | |
1735 | // 32X ROM (banked) |
5e49c3a8 |
1736 | bank_switch(0); |
b78efee2 |
1737 | |
83ff19ec |
1738 | // SYS regs |
1739 | cpu68k_map_set(m68k_read8_map, 0xa10000, 0xa1ffff, PicoRead8_32x_on, 1); |
1740 | cpu68k_map_set(m68k_read16_map, 0xa10000, 0xa1ffff, PicoRead16_32x_on, 1); |
1741 | cpu68k_map_set(m68k_write8_map, 0xa10000, 0xa1ffff, PicoWrite8_32x_on, 1); |
1742 | cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, PicoWrite16_32x_on, 1); |
1743 | |
bcf65fd6 |
1744 | // SH2 maps: A31,A30,A29,CS1,CS0 |
1745 | // all unmapped by default |
e05b81fc |
1746 | for (i = 0; i < ARRAY_SIZE(sh2_read8_map); i++) { |
bcf65fd6 |
1747 | sh2_read8_map[i].addr = MAP_HANDLER(sh2_read8_unmapped); |
1748 | sh2_read16_map[i].addr = MAP_HANDLER(sh2_read16_unmapped); |
e05b81fc |
1749 | } |
1750 | |
1751 | for (i = 0; i < ARRAY_SIZE(sh2_write8_map); i++) { |
f4bb5d6b |
1752 | sh2_write8_map[i] = sh2_write8_unmapped; |
1753 | sh2_write16_map[i] = sh2_write16_unmapped; |
bcf65fd6 |
1754 | } |
1755 | |
4b315c21 |
1756 | // "purge area" |
e05b81fc |
1757 | for (i = 0x40; i <= 0x5f; i++) { |
1758 | sh2_write8_map[i >> 1] = |
1759 | sh2_write16_map[i >> 1] = sh2_write_ignore; |
4b315c21 |
1760 | } |
1761 | |
bcf65fd6 |
1762 | // CS0 |
1763 | sh2_read8_map[0].addr = sh2_read8_map[4].addr = MAP_HANDLER(sh2_read8_cs0); |
1764 | sh2_read16_map[0].addr = sh2_read16_map[4].addr = MAP_HANDLER(sh2_read16_cs0); |
e05b81fc |
1765 | sh2_write8_map[0x00/2] = sh2_write8_map[0x20/2] = sh2_write8_cs0; |
1766 | sh2_write16_map[0x00/2] = sh2_write16_map[0x20/2] = sh2_write16_cs0; |
bcf65fd6 |
1767 | // CS1 - ROM |
1768 | sh2_read8_map[1].addr = sh2_read8_map[5].addr = |
1769 | sh2_read16_map[1].addr = sh2_read16_map[5].addr = MAP_MEMORY(Pico.rom); |
1770 | sh2_read8_map[1].mask = sh2_read8_map[5].mask = |
1771 | sh2_read16_map[1].mask = sh2_read16_map[5].mask = 0x3fffff; // FIXME |
1772 | // CS2 - DRAM - done by Pico32xSwapDRAM() |
1773 | sh2_read8_map[2].mask = sh2_read8_map[6].mask = |
1774 | sh2_read16_map[2].mask = sh2_read16_map[6].mask = 0x01ffff; |
1775 | // CS3 - SDRAM |
1776 | sh2_read8_map[3].addr = sh2_read8_map[7].addr = |
f4bb5d6b |
1777 | sh2_read16_map[3].addr = sh2_read16_map[7].addr = MAP_MEMORY(Pico32xMem->sdram); |
e05b81fc |
1778 | sh2_write8_map[0x06/2] = sh2_write8_map[0x26/2] = sh2_write8_sdram; |
1779 | sh2_write16_map[0x06/2] = sh2_write16_map[0x26/2] = sh2_write16_sdram; |
bcf65fd6 |
1780 | sh2_read8_map[3].mask = sh2_read8_map[7].mask = |
f4bb5d6b |
1781 | sh2_read16_map[3].mask = sh2_read16_map[7].mask = 0x03ffff; |
bcf65fd6 |
1782 | // SH2 data array |
1783 | sh2_read8_map[0x18].addr = MAP_HANDLER(sh2_read8_da); |
1784 | sh2_read16_map[0x18].addr = MAP_HANDLER(sh2_read16_da); |
e05b81fc |
1785 | sh2_write8_map[0xc0/2] = sh2_write8_da; |
1786 | sh2_write16_map[0xc0/2] = sh2_write16_da; |
bcf65fd6 |
1787 | // SH2 IO |
1788 | sh2_read8_map[0x1f].addr = MAP_HANDLER(sh2_peripheral_read8); |
1789 | sh2_read16_map[0x1f].addr = MAP_HANDLER(sh2_peripheral_read16); |
e05b81fc |
1790 | sh2_write8_map[0xff/2] = sh2_peripheral_write8; |
1791 | sh2_write16_map[0xff/2] = sh2_peripheral_write16; |
bcf65fd6 |
1792 | |
1793 | // map DRAM area, both 68k and SH2 |
1794 | Pico32xSwapDRAM(1); |
1795 | |
1796 | msh2.read8_map = ssh2.read8_map = sh2_read8_map; |
1797 | msh2.read16_map = ssh2.read16_map = sh2_read16_map; |
23686515 |
1798 | msh2.write8_tab = ssh2.write8_tab = (const void **)(void *)sh2_write8_map; |
1799 | msh2.write16_tab = ssh2.write16_tab = (const void **)(void *)sh2_write16_map; |
bcf65fd6 |
1800 | |
23686515 |
1801 | sh2_drc_mem_setup(&msh2); |
1802 | sh2_drc_mem_setup(&ssh2); |
be2c4208 |
1803 | } |
1804 | |
27e26273 |
1805 | void Pico32xMemStateLoaded(void) |
b4db550e |
1806 | { |
1807 | bank_switch(Pico32x.regs[4 / 2]); |
1808 | Pico32xSwapDRAM((Pico32x.vdp_regs[0x0a / 2] & P32XV_FS) ^ P32XV_FS); |
b4db550e |
1809 | memset(Pico32xMem->pwm, 0, sizeof(Pico32xMem->pwm)); |
27e26273 |
1810 | Pico32x.dirty_pal = 1; |
51d86e55 |
1811 | |
19886062 |
1812 | Pico32x.emu_flags &= ~(P32XF_68KCPOLL | P32XF_68KVPOLL); |
1813 | memset(&m68k_poll, 0, sizeof(m68k_poll)); |
1814 | msh2.state = 0; |
1815 | msh2.poll_addr = msh2.poll_cycles = msh2.poll_cnt = 0; |
1816 | ssh2.state = 0; |
1817 | ssh2.poll_addr = ssh2.poll_cycles = ssh2.poll_cnt = 0; |
1818 | |
b4db550e |
1819 | sh2_drc_flush_all(); |
b4db550e |
1820 | } |
1821 | |
ed4402a7 |
1822 | // vim:shiftwidth=2:ts=2:expandtab |