cff531af |
1 | /* |
2 | * SMS emulation |
3 | * (C) notaz, 2009-2010 |
7bf552b5 |
4 | * (C) irixxxx, 2021-2024 |
cff531af |
5 | * |
6 | * This work is licensed under the terms of MAME license. |
7 | * See COPYING file in the top-level directory. |
8 | */ |
87b0845f |
9 | /* |
10 | * TODO: |
1cdc3f84 |
11 | * - start in a state as if BIOS ran (partly done for VDP registers, RAM) |
12 | * - region support (currently only very limited PAL and Mark-III support) |
f9ea940f |
13 | * - mapper for EEPROM support |
87b0845f |
14 | */ |
3e49ffd0 |
15 | #include "pico_int.h" |
af37bca8 |
16 | #include "memory.h" |
2ec9bec5 |
17 | #include "sound/sn76496.h" |
a2f24bfa |
18 | #include "sound/emu2413/emu2413.h" |
19 | |
20 | extern void YM2413_regWrite(unsigned reg); |
21 | extern void YM2413_dataWrite(unsigned data); |
22 | |
1ce2b092 |
23 | extern unsigned sprites_status; // TODO put in some hdr file! |
a2f24bfa |
24 | |
2ec9bec5 |
25 | static unsigned char vdp_data_read(void) |
3e49ffd0 |
26 | { |
2ec9bec5 |
27 | struct PicoVideo *pv = &Pico.video; |
28 | unsigned char d; |
29 | |
1c120817 |
30 | d = Pico.ms.vdp_buffer; |
31 | Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)]; |
2ec9bec5 |
32 | pv->addr = (pv->addr + 1) & 0x3fff; |
33 | pv->pending = 0; |
34 | return d; |
35 | } |
36 | |
37 | static unsigned char vdp_ctl_read(void) |
38 | { |
1c25c32c |
39 | struct PicoVideo *pv = &Pico.video; |
40 | unsigned char d; |
41 | |
cf83610b |
42 | z80_int_assert(0); |
1c25c32c |
43 | d = pv->status | (pv->pending_ints << 7); |
44 | pv->pending = pv->pending_ints = 0; |
45 | pv->status = 0; |
2ec9bec5 |
46 | |
1c120817 |
47 | if (pv->reg[0] & 0x04) |
48 | d |= 0x1f; // unused bits in mode 4 read as 1 |
49 | |
2ec9bec5 |
50 | elprintf(EL_SR, "VDP sr: %02x", d); |
51 | return d; |
52 | } |
53 | |
54 | static void vdp_data_write(unsigned char d) |
55 | { |
56 | struct PicoVideo *pv = &Pico.video; |
57 | |
58 | if (pv->type == 3) { |
ace18401 |
59 | // cram. 32 on SMS, but 64 on MD. Fill 2nd half of cram for prio bit mirror |
0aa63fce |
60 | if (PicoIn.AHW & PAHW_GG) { // GG, same layout as MD |
466fa079 |
61 | unsigned a = pv->addr & 0x3f; |
e4da4fe8 |
62 | if (a & 0x1) { // write complete color on high byte write |
1c120817 |
63 | u16 c = ((d&0x0f) << 8) | Pico.ms.vdp_buffer; |
e4da4fe8 |
64 | if (PicoMem.cram[a >> 1] != c) Pico.m.dirtyPal = 1; |
ace18401 |
65 | PicoMem.cram[a >> 1] = PicoMem.cram[(a >> 1)+0x20] = c; |
e4da4fe8 |
66 | } |
466fa079 |
67 | } else { // SMS, convert to MD layout (00BbGgRr to 0000BbBbGgGgRrRr) |
68 | unsigned a = pv->addr & 0x1f; |
e4da4fe8 |
69 | u16 c = ((d&0x30)<<6) + ((d&0x0c)<<4) + ((d&0x03)<<2); |
466fa079 |
70 | if (PicoMem.cram[a] != (c | (c>>2))) Pico.m.dirtyPal = 1; |
ace18401 |
71 | PicoMem.cram[a] = PicoMem.cram[a+0x20] = c | (c>>2); |
466fa079 |
72 | } |
2ec9bec5 |
73 | } else { |
57c5a5e5 |
74 | PicoMem.vramb[MEM_LE2(pv->addr)] = d; |
2ec9bec5 |
75 | } |
76 | pv->addr = (pv->addr + 1) & 0x3fff; |
2ec9bec5 |
77 | |
1c120817 |
78 | Pico.ms.vdp_buffer = d; |
2ec9bec5 |
79 | pv->pending = 0; |
80 | } |
81 | |
cf83610b |
82 | static NOINLINE void vdp_reg_write(struct PicoVideo *pv, u8 a, u8 d) |
83 | { |
84 | int l; |
85 | |
86 | pv->reg[a] = d; |
87 | switch (a) { |
88 | case 0: |
89 | l = pv->pending_ints & (d >> 3) & 2; |
90 | elprintf(EL_INTS, "hint %d", l); |
91 | z80_int_assert(l); |
92 | break; |
93 | case 1: |
94 | l = pv->pending_ints & (d >> 5) & 1; |
95 | elprintf(EL_INTS, "vint %d", l); |
96 | z80_int_assert(l); |
97 | break; |
98 | } |
99 | } |
100 | |
101 | static void vdp_ctl_write(u8 d) |
2ec9bec5 |
102 | { |
103 | struct PicoVideo *pv = &Pico.video; |
104 | |
105 | if (pv->pending) { |
e4da4fe8 |
106 | pv->type = d >> 6; |
107 | if (pv->type == 2) { |
200772b7 |
108 | elprintf(EL_IO, " VDP r%02x=%02x", d & 0x0f, pv->addr & 0xff); |
cf83610b |
109 | if (pv->reg[d & 0x0f] != (u8)pv->addr) |
110 | vdp_reg_write(pv, d & 0x0f, pv->addr); |
2ec9bec5 |
111 | } |
2ec9bec5 |
112 | pv->addr &= 0x00ff; |
113 | pv->addr |= (d & 0x3f) << 8; |
e4da4fe8 |
114 | if (pv->type == 0) { |
1c120817 |
115 | Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)]; |
e4da4fe8 |
116 | pv->addr = (pv->addr + 1) & 0x3fff; |
117 | } |
2ec9bec5 |
118 | } else { |
119 | pv->addr &= 0x3f00; |
120 | pv->addr |= d; |
121 | } |
122 | pv->pending ^= 1; |
3e49ffd0 |
123 | } |
124 | |
1cdc3f84 |
125 | static u8 vdp_hcounter(int cycles) |
126 | { |
127 | // 171 slots per scanline of 228 clocks, counted 0xf4-0x93, 0xe9-0xf3 |
128 | // this matches h counter tables in SMSVDPTest: |
129 | // hc = (cycles+2) * 171 /228 -1 + 0xf4; |
130 | int hc = (((cycles+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing |
131 | if (hc > 0x193) hc += 0xe9-0x93-1; |
132 | return hc; |
133 | } |
134 | |
2ec9bec5 |
135 | static unsigned char z80_sms_in(unsigned short a) |
3e49ffd0 |
136 | { |
1c120817 |
137 | unsigned char d = 0xff; |
2ec9bec5 |
138 | |
1c120817 |
139 | a &= 0xff; |
200772b7 |
140 | elprintf(EL_IO, "z80 port %04x read", a); |
1c120817 |
141 | if(a >= 0xf0){ |
c9076db9 |
142 | if (Pico.m.hardware & PMS_HW_FM) { |
1c120817 |
143 | switch(a) |
81d54be1 |
144 | { |
145 | case 0xf0: |
146 | // FM reg port |
147 | break; |
148 | case 0xf1: |
149 | // FM data port |
150 | break; |
151 | case 0xf2: |
152 | // bit 0 = 1 active FM Pac |
1c120817 |
153 | d = 0xf8 | Pico.ms.fm_ctl; |
81d54be1 |
154 | break; |
a2f24bfa |
155 | } |
a2f24bfa |
156 | } |
157 | } |
158 | else{ |
1c120817 |
159 | switch (a & 0xc1) |
a2f24bfa |
160 | { |
161 | case 0x00: |
162 | case 0x01: |
0aa63fce |
163 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) { // GG I/O area |
1c120817 |
164 | switch (a) { |
1603058a |
165 | case 0: d = 0xff & ~(PicoIn.pad[0] & 0x80); break; |
166 | case 1: d = Pico.ms.io_gg[1] | (Pico.ms.io_gg[2] & 0x7f); break; |
167 | case 5: d = Pico.ms.io_gg[5] & 0xf8; break; |
168 | default: d = Pico.ms.io_gg[a]; break; |
1c120817 |
169 | } |
170 | } |
a2f24bfa |
171 | break; |
172 | |
173 | case 0x40: /* V counter */ |
174 | d = Pico.video.v_counter; |
175 | elprintf(EL_HVCNT, "V counter read: %02x", d); |
176 | break; |
177 | |
cc1547e8 |
178 | case 0x41: /* H counter */ |
1bb3f2cc |
179 | d = Pico.ms.vdp_hlatch; |
a2f24bfa |
180 | elprintf(EL_HVCNT, "H counter read: %02x", d); |
181 | break; |
182 | |
183 | case 0x80: |
184 | d = vdp_data_read(); |
185 | break; |
186 | |
187 | case 0x81: |
188 | d = vdp_ctl_read(); |
189 | break; |
190 | |
191 | case 0xc0: /* I/O port A and B */ |
4af69a1d |
192 | if (! (PicoIn.AHW & PAHW_SC) || (Pico.ms.io_sg & 7) == 7) { |
cab84f29 |
193 | d = ~((PicoIn.pad[0] & 0x3f) | (PicoIn.pad[1] << 6)); |
4af69a1d |
194 | if (!(Pico.ms.io_ctl & 0x01)) // TR as output |
195 | d = (d & ~0x20) | ((Pico.ms.io_ctl << 1) & 0x20); |
196 | } else |
cab84f29 |
197 | ; // read kbd 8 bits |
a2f24bfa |
198 | break; |
199 | |
200 | case 0xc1: /* I/O port B and miscellaneous */ |
0aa63fce |
201 | if (! (PicoIn.AHW & PAHW_SC) || (Pico.ms.io_sg & 7) == 7) { |
cab84f29 |
202 | d = (Pico.ms.io_ctl & 0x80) | ((Pico.ms.io_ctl << 1) & 0x40) | 0x30; |
203 | d |= ~(PicoIn.pad[1] >> 2) & 0x0f; |
4af69a1d |
204 | if (!(Pico.ms.io_ctl & 0x04)) // TR as output |
205 | d = (d & ~0x08) | ((Pico.ms.io_ctl >> 3) & 0x08); |
cab84f29 |
206 | if (Pico.ms.io_ctl & 0x08) d |= 0x80; // TH as input is unconnected |
207 | if (Pico.ms.io_ctl & 0x02) d |= 0x40; |
208 | } else |
209 | ; // read kbd 4 bits |
a2f24bfa |
210 | break; |
211 | } |
2ec9bec5 |
212 | } |
200772b7 |
213 | elprintf(EL_IO, "ret = %02x", d); |
2ec9bec5 |
214 | return d; |
215 | } |
216 | |
217 | static void z80_sms_out(unsigned short a, unsigned char d) |
218 | { |
200772b7 |
219 | elprintf(EL_IO, "z80 port %04x write %02x", a, d); |
2ec9bec5 |
220 | |
1c120817 |
221 | a &= 0xff; |
cab84f29 |
222 | if (a >= 0xf0){ |
c9076db9 |
223 | if (Pico.m.hardware & PMS_HW_FM) { |
1c120817 |
224 | switch(a) |
81d54be1 |
225 | { |
226 | case 0xf0: |
227 | // FM reg port |
43ef4071 |
228 | Pico.m.hardware |= PMS_HW_FMUSED; |
81d54be1 |
229 | YM2413_regWrite(d); |
81d54be1 |
230 | break; |
231 | case 0xf1: |
232 | // FM data port |
233 | YM2413_dataWrite(d); |
81d54be1 |
234 | break; |
235 | case 0xf2: |
236 | // bit 0 = 1 active FM Pac |
1c120817 |
237 | Pico.ms.fm_ctl = d & 0x1; |
81d54be1 |
238 | break; |
239 | } |
a2f24bfa |
240 | } |
241 | } |
cab84f29 |
242 | else { |
1c120817 |
243 | switch (a & 0xc1) |
a2f24bfa |
244 | { |
1c120817 |
245 | case 0x00: |
0aa63fce |
246 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) // GG I/O area |
1c120817 |
247 | Pico.ms.io_gg[a] = d; |
70efc52d |
248 | if ((PicoIn.AHW & PAHW_GG) && a == 0x6) |
249 | SN76496Config(d); |
1c120817 |
250 | break; |
a2f24bfa |
251 | case 0x01: |
0aa63fce |
252 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) { // GG I/O area |
1c120817 |
253 | Pico.ms.io_gg[a] = d; |
254 | } else { |
255 | // pad. latch hcounter if one of the TH lines is switched to 1 |
1cdc3f84 |
256 | if ((Pico.ms.io_ctl ^ d) & d & 0xa0) |
7eeb85be |
257 | Pico.ms.vdp_hlatch = vdp_hcounter(z80_cyclesDone() - Pico.t.z80c_line_start); |
1c120817 |
258 | Pico.ms.io_ctl = d; |
cc1547e8 |
259 | } |
a2f24bfa |
260 | break; |
261 | |
262 | case 0x40: |
263 | case 0x41: |
1bb3f2cc |
264 | PsndDoPSG(z80_cyclesDone()); |
a2f24bfa |
265 | SN76496Write(d); |
266 | break; |
267 | |
268 | case 0x80: |
269 | vdp_data_write(d); |
270 | break; |
271 | |
272 | case 0x81: |
273 | vdp_ctl_write(d); |
274 | break; |
cab84f29 |
275 | |
276 | case 0xc0: |
0aa63fce |
277 | if ((PicoIn.AHW & PAHW_SC) && (a & 0x2)) |
cab84f29 |
278 | Pico.ms.io_sg = d; // 0xc2 = kbd/pad select |
a2f24bfa |
279 | } |
2ec9bec5 |
280 | } |
281 | } |
282 | |
7eeb85be |
283 | static void z80_exec(int aim) |
284 | { |
285 | Pico.t.z80c_aim = aim; |
286 | Pico.t.z80c_cnt += z80_run(Pico.t.z80c_aim - Pico.t.z80c_cnt); |
287 | } |
288 | |
f9ea940f |
289 | |
290 | // ROM/SRAM bank mapping, see https://www.smspower.org/Development/Mappers |
291 | |
87b0845f |
292 | static int bank_mask; |
293 | |
b784d4a5 |
294 | static void xwrite(unsigned int a, unsigned char d); |
295 | |
f9ea940f |
296 | |
297 | // Sega mapper. Maps 3 banks 16KB each, with SRAM support |
298 | static void write_sram_sega(unsigned short a, unsigned char d) |
b784d4a5 |
299 | { |
300 | // SRAM is mapped in 2 16KB banks, selected by bit 2 in control reg |
301 | a &= 0x3fff; |
302 | a += ((Pico.ms.carthw[0x0c] & 0x04) >> 2) * 0x4000; |
303 | |
304 | Pico.sv.changed |= (Pico.sv.data[a] != d); |
305 | Pico.sv.data[a] = d; |
306 | } |
307 | |
032c76a3 |
308 | static void write_bank_sega(unsigned short a, unsigned char d) |
2ec9bec5 |
309 | { |
f9ea940f |
310 | if (a < 0xfff8) return; |
1cdc3f84 |
311 | // avoid mapper detection for RAM fill with 0 |
312 | if (Pico.ms.mapper != PMS_MAP_SEGA && (Pico.ms.mapper || d == 0)) return; |
f9ea940f |
313 | |
314 | elprintf(EL_Z80BNK, "bank sega %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
315 | Pico.ms.mapper = PMS_MAP_SEGA; |
4af69a1d |
316 | if (d == Pico.ms.carthw[a & 0x0f]) return; |
b784d4a5 |
317 | Pico.ms.carthw[a & 0x0f] = d; |
8ba60e31 |
318 | |
2ec9bec5 |
319 | switch (a & 0x0f) |
320 | { |
2ec9bec5 |
321 | case 0x0d: |
b784d4a5 |
322 | d &= bank_mask; |
323 | z80_map_set(z80_read_map, 0x0400, 0x3fff, Pico.rom+0x400 + (d << 14), 0); |
2ec9bec5 |
324 | break; |
325 | case 0x0e: |
87b0845f |
326 | d &= bank_mask; |
2ec9bec5 |
327 | z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0); |
2ec9bec5 |
328 | break; |
b784d4a5 |
329 | |
330 | case 0x0c: |
331 | if (d & ~0x8c) |
332 | elprintf(EL_STATUS|EL_ANOMALY, "%02x written to control reg!", d); |
333 | /*FALLTHROUGH*/ |
2ec9bec5 |
334 | case 0x0f: |
b784d4a5 |
335 | if (Pico.ms.carthw[0xc] & 0x08) { |
336 | d = (Pico.ms.carthw[0xc] & 0x04) >> 2; |
337 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.sv.data + d*0x4000, 0); |
f9ea940f |
338 | z80_map_set(z80_write_map, 0x8000, 0xbfff, write_sram_sega, 1); |
b784d4a5 |
339 | } else { |
340 | d = Pico.ms.carthw[0xf] & bank_mask; |
341 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); |
b784d4a5 |
342 | z80_map_set(z80_write_map, 0x8000, 0xbfff, xwrite, 1); |
343 | } |
2ec9bec5 |
344 | break; |
345 | } |
3e49ffd0 |
346 | } |
347 | |
25559e54 |
348 | // Codemasters mapper. Similar to Sega, but different addresses |
1cdc3f84 |
349 | static void write_bank_codem(unsigned short a, unsigned char d) |
350 | { |
f9ea940f |
351 | if (a >= 0xc000 || (a & 0x3fff)) return; // address is 0x0000, 0x4000, 0x8000? |
352 | // don't detect linear mapping to avoid confusing with MSX |
353 | if (Pico.ms.mapper != PMS_MAP_CODEM && (Pico.ms.mapper || (a>>14) == d)) return; |
354 | elprintf(EL_Z80BNK, "bank codem %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
355 | Pico.ms.mapper = PMS_MAP_CODEM; |
4af69a1d |
356 | if (Pico.ms.carthw[a>>14] == d) return; |
1cdc3f84 |
357 | Pico.ms.carthw[a>>14] = d; |
358 | |
359 | d &= bank_mask; |
360 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
25559e54 |
361 | if (Pico.ms.carthw[1] & 0x80) { |
362 | z80_map_set(z80_read_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0); |
363 | z80_map_set(z80_write_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0); |
364 | } else { |
365 | d = Pico.ms.carthw[2] & bank_mask; |
366 | z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + (d << 14)+0x2000, 0); |
367 | z80_map_set(z80_write_map, 0xa000, 0xbfff, xwrite, 1); |
368 | } |
1cdc3f84 |
369 | } |
370 | |
f9ea940f |
371 | // MSX mapper. 4 selectable 8KB banks at the top |
032c76a3 |
372 | static void write_bank_msx(unsigned short a, unsigned char d) |
373 | { |
67c9ba38 |
374 | if (a > 0x0003) return; |
f9ea940f |
375 | // don't detect linear mapping to avoid confusing with Codemasters |
df6c895c |
376 | if (Pico.ms.mapper != PMS_MAP_MSX && (Pico.ms.mapper || (a|d) == 0 || d >= 0x80)) return; |
f9ea940f |
377 | elprintf(EL_Z80BNK, "bank msx %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
378 | Pico.ms.mapper = PMS_MAP_MSX; |
032c76a3 |
379 | Pico.ms.carthw[a] = d; |
380 | |
381 | a = (a^2)*0x2000 + 0x4000; |
382 | d &= 2*bank_mask + 1; |
383 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
384 | } |
385 | |
f9ea940f |
386 | // Korea mapping, 1 selectable 16KB bank at the top |
387 | static void write_bank_korea(unsigned short a, unsigned char d) |
388 | { |
389 | if (a != 0xa000) return; |
390 | if (Pico.ms.mapper != PMS_MAP_KOREA && (Pico.ms.mapper)) return; |
391 | elprintf(EL_Z80BNK, "bank korea %04x %02x @ %04x", a, d, z80_pc()); |
392 | Pico.ms.mapper = PMS_MAP_KOREA; |
393 | Pico.ms.carthw[0xf] = d; |
394 | |
395 | d &= bank_mask; |
396 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); |
397 | } |
398 | |
399 | // Korean n-in-1 mapping. 1 selectable 32KB bank at the bottom |
1cdc3f84 |
400 | static void write_bank_n32k(unsigned short a, unsigned char d) |
1bb3f2cc |
401 | { |
f9ea940f |
402 | if (a != 0xffff) return; |
1cdc3f84 |
403 | // code must be in RAM since all visible ROM space is swapped |
404 | if (Pico.ms.mapper != PMS_MAP_N32K && (Pico.ms.mapper || z80_pc() < 0xc000)) return; |
1bb3f2cc |
405 | elprintf(EL_Z80BNK, "bank 32k %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
406 | Pico.ms.mapper = PMS_MAP_N32K; |
1bb3f2cc |
407 | Pico.ms.carthw[0xf] = d; |
408 | |
409 | d &= bank_mask >> 1; |
1cdc3f84 |
410 | z80_map_set(z80_read_map, 0, 0x7fff, Pico.rom + (d << 15), 0); |
411 | } |
412 | |
f9ea940f |
413 | // Korean 4-in-1. 2 selectable 16KB banks, top bank is shifted by bottom one |
1cdc3f84 |
414 | static void write_bank_n16k(unsigned short a, unsigned char d) |
415 | { |
f9ea940f |
416 | if (a != 0x3ffe && a != 0x7fff && a != 0xbfff) return; |
1cdc3f84 |
417 | // code must be in RAM since all visible ROM space is swapped |
418 | if (Pico.ms.mapper != PMS_MAP_N16K && (Pico.ms.mapper || z80_pc() < 0xc000)) return; |
419 | elprintf(EL_Z80BNK, "bank 16k %04x %02x @ %04x", a, d, z80_pc()); |
420 | Pico.ms.mapper = PMS_MAP_N16K; |
421 | Pico.ms.carthw[a>>14] = d; |
422 | |
423 | d &= bank_mask; |
424 | a = a & 0xc000; |
425 | // the top bank shifts with the bottom bank. |
426 | if (a == 0x8000) d += Pico.ms.carthw[0] & 0x30; |
427 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
1bb3f2cc |
428 | } |
429 | |
f9ea940f |
430 | // MSX-Nemesis mapper. 4 selectable 8KB banks at the top |
431 | static void write_bank_msxn(unsigned short a, unsigned char d) |
432 | { |
433 | if (a > 0x0003) return; |
434 | // never autodetected, selectable only via config |
435 | if (Pico.ms.mapper != PMS_MAP_NEMESIS) return; |
436 | elprintf(EL_Z80BNK, "bank nems %04x %02x @ %04x", a, d, z80_pc()); |
437 | Pico.ms.carthw[a] = d; |
438 | |
439 | a = (a^2)*0x2000 + 0x4000; |
440 | d &= 2*bank_mask + 1; |
441 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
442 | } |
443 | |
444 | // Korean Janggun mapper. 4 selectable 8KB banks at the top, hardware byte flip |
445 | static unsigned char read_flipped_jang(unsigned a) |
446 | { |
447 | static unsigned char flipper[16] = // reversed nibble bit order |
448 | { 0x0,0x8,0x4,0xc,0x2,0xa,0x6,0xe,0x1,0x9,0x5,0xd,0x3,0xb,0x7,0xf }; |
449 | unsigned char c; |
450 | |
451 | // return value at address a in reversed bit order |
452 | c = Pico.rom[(Pico.ms.carthw[a>>13] << 13) + (a & 0x1fff)]; |
453 | return (flipper[c&0xf]<<4) | flipper[c>>4]; |
454 | } |
455 | |
456 | static void write_bank_jang(unsigned short a, unsigned char d) |
457 | { |
458 | // address is 0xfffe, 0xffff, 0x4000, 0x6000, 0x8000, 0xa000 |
b7209504 |
459 | if ((a|1) != 0xffff && (a < 0x4000 || a > 0xa000 || (a & 0x1fff))) return; |
f9ea940f |
460 | // never autodetected, selectable only via config |
461 | if (Pico.ms.mapper != PMS_MAP_JANGGUN) return; |
462 | elprintf(EL_Z80BNK, "bank jang %04x %02x @ %04x", a, d, z80_pc()); |
463 | |
464 | if ((a|1) == 0xffff) { |
465 | int x = a & 1, f = d & 0x40; |
466 | Pico.ms.carthw[x] = d; |
467 | d &= bank_mask; |
468 | Pico.ms.carthw[2*x + 2] = 2*d, Pico.ms.carthw[2*x + 3] = 2*d+1; |
469 | a = (x+1) * 0x4000; |
470 | if (!f) |
471 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
472 | else |
473 | z80_map_set(z80_read_map, a, a+0x3fff, read_flipped_jang, 1); |
474 | } else { |
475 | d &= 2*bank_mask + 1; |
476 | Pico.ms.carthw[a>>13] = d; |
477 | if (!(Pico.ms.carthw[(a>>15)&1] & 0x40)) |
478 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
479 | else |
480 | z80_map_set(z80_read_map, a, a+0x1fff, read_flipped_jang, 1); |
481 | } |
482 | } |
483 | |
7c6f7914 |
484 | // Korean 188-in-1. 4 8KB banks from 0x4000, selected by xor'd bank index |
485 | static void write_bank_xor(unsigned short a, unsigned char d) |
486 | { |
487 | // 4x8KB bank select @0x2000 |
b7209504 |
488 | if ((a&0xff00) != 0x2000) return; |
7c6f7914 |
489 | if (Pico.ms.mapper != PMS_MAP_XOR && Pico.ms.mapper) return; |
490 | |
96948bdf |
491 | elprintf(EL_Z80BNK, "bank xor %04x %02x @ %04x", a, d, z80_pc()); |
7c6f7914 |
492 | Pico.ms.mapper = PMS_MAP_XOR; |
493 | |
494 | Pico.ms.carthw[0] = d; |
495 | z80_map_set(z80_read_map, 0x4000, 0x5fff, Pico.rom + ((d^0x1f) << 13), 0); |
496 | z80_map_set(z80_read_map, 0x6000, 0x7fff, Pico.rom + ((d^0x1e) << 13), 0); |
497 | z80_map_set(z80_read_map, 0x8000, 0x9fff, Pico.rom + ((d^0x1d) << 13), 0); |
498 | z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + ((d^0x1c) << 13), 0); |
499 | } |
500 | |
3611781e |
501 | // SG-1000 8KB RAM Adaptor mapper. 8KB RAM at address 0x2000 |
aaef4b94 |
502 | static void write_bank_x8k(unsigned short a, unsigned char d) |
503 | { |
df6c895c |
504 | // 8KB address range @ 0x2000 (adaptor) or @ 0x8000 (cartridge) |
b7209504 |
505 | if (((a&0xe000) != 0x2000 && (a&0xe000) != 0x8000) || (a & 0x0f) == 5) return; |
df6c895c |
506 | if (Pico.ms.mapper != PMS_MAP_8KBRAM && Pico.ms.mapper) return; |
507 | |
171fb8cc |
508 | elprintf(EL_Z80BNK, "bank x8k %04x %02x @ %04x", a, d, z80_pc()); |
7c16d135 |
509 | ((unsigned char *)(PicoMem.vram+0x4000))[a&0x1fff] = d; |
3611781e |
510 | Pico.ms.mapper = PMS_MAP_8KBRAM; |
171fb8cc |
511 | |
216c9f17 |
512 | a &= 0xe000; |
df6c895c |
513 | Pico.ms.carthw[0] = a >> 12; |
216c9f17 |
514 | z80_map_set(z80_read_map, a, a+0x1fff, PicoMem.vram+0x4000, 0); |
515 | z80_map_set(z80_write_map, a, a+0x1fff, PicoMem.vram+0x4000, 0); |
aaef4b94 |
516 | } |
517 | |
cab84f29 |
518 | // SC-3000 32KB RAM mapper for BASIC level IIIB. 32KB RAM at address 0x8000 |
519 | static void write_bank_x32k(unsigned short a, unsigned char d) |
520 | { |
521 | // 32KB address range @ 0x8000 |
522 | if ((a&0xc000) != 0x8000) return; |
523 | if (Pico.ms.mapper != PMS_MAP_32KBRAM && |
524 | (Pico.ms.mapper || Pico.romsize > 0x8000)) return; |
525 | |
526 | elprintf(EL_Z80BNK, "bank x32k %04x %02x @ %04x", a, d, z80_pc()); |
527 | ((unsigned char *)(PicoMem.vram+0x4000))[a&0x7fff] = d; |
528 | Pico.ms.mapper = PMS_MAP_32KBRAM; |
529 | |
530 | a &= 0xc000; |
531 | Pico.ms.carthw[0] = a >> 12; |
532 | // NB this deactivates internal RAM and all mapper detection |
533 | z80_map_set(z80_read_map, a, a+0x7fff, PicoMem.vram+0x4000, 0); |
534 | z80_map_set(z80_write_map, a, a+0x7fff, PicoMem.vram+0x4000, 0); |
535 | } |
536 | |
df6c895c |
537 | char *mappers[] = { |
538 | [PMS_MAP_SEGA] = "Sega", |
539 | [PMS_MAP_CODEM] = "Codemasters", |
540 | [PMS_MAP_KOREA] = "Korea", |
541 | [PMS_MAP_MSX] = "Korea MSX", |
542 | [PMS_MAP_N32K] = "Korea X-in-1", |
543 | [PMS_MAP_N16K] = "Korea 4-Pak", |
544 | [PMS_MAP_JANGGUN] = "Korea Janggun", |
545 | [PMS_MAP_NEMESIS] = "Korea Nemesis", |
546 | [PMS_MAP_8KBRAM] = "Taiwan 8K RAM", |
7c6f7914 |
547 | [PMS_MAP_XOR] = "Korea XOR", |
cab84f29 |
548 | [PMS_MAP_32KBRAM] = "Sega 32K RAM", |
df6c895c |
549 | }; |
550 | |
f9ea940f |
551 | // TODO auto-selecting is not really reliable. |
1cdc3f84 |
552 | // Before adding more mappers this should be revised. |
553c3eaa |
553 | static void xwrite(unsigned int a, unsigned char d) |
2ec9bec5 |
554 | { |
b7209504 |
555 | int sz = (/*PicoIn.AHW & (PAHW_SG|PAHW_SC) ? 2 :*/ 8) * 1024; |
0aa63fce |
556 | |
200772b7 |
557 | elprintf(EL_IO, "z80 write [%04x] %02x", a, d); |
2ec9bec5 |
558 | if (a >= 0xc000) |
0aa63fce |
559 | PicoMem.zram[a & (sz-1)] = d; |
032c76a3 |
560 | |
f9ea940f |
561 | switch (Pico.ms.mapper) { // via config, or auto detected |
562 | case PMS_MAP_SEGA: write_bank_sega(a, d); break; |
563 | case PMS_MAP_CODEM: write_bank_codem(a, d); break; |
564 | case PMS_MAP_MSX: write_bank_msx(a, d); break; |
565 | case PMS_MAP_KOREA: write_bank_korea(a, d); break; |
566 | case PMS_MAP_N32K: write_bank_n32k(a, d); break; |
567 | case PMS_MAP_N16K: write_bank_n16k(a, d); break; |
568 | case PMS_MAP_JANGGUN: write_bank_jang(a, d); break; |
569 | case PMS_MAP_NEMESIS: write_bank_msxn(a, d); break; |
171fb8cc |
570 | case PMS_MAP_8KBRAM: write_bank_x8k(a, d); break; |
cab84f29 |
571 | case PMS_MAP_32KBRAM: write_bank_x32k(a, d); break; |
7c6f7914 |
572 | case PMS_MAP_XOR: write_bank_xor(a, d); break; |
f9ea940f |
573 | |
574 | case PMS_MAP_AUTO: |
df6c895c |
575 | // disable autodetection after some time |
b7209504 |
576 | if ((a >= 0xc000 && a < 0xfff8) || Pico.ms.mapcnt > 50) break; |
f9ea940f |
577 | // NB the sequence of mappers is crucial for the auto detection |
0aa63fce |
578 | if (PicoIn.AHW & PAHW_SC) { |
cab84f29 |
579 | write_bank_x32k(a,d); |
0aa63fce |
580 | } else if (PicoIn.AHW & PAHW_SG) { |
df6c895c |
581 | write_bank_x8k(a, d); |
cab84f29 |
582 | } else { |
df6c895c |
583 | write_bank_n32k(a, d); |
584 | write_bank_sega(a, d); |
585 | write_bank_msx(a, d); |
586 | write_bank_codem(a, d); |
587 | write_bank_korea(a, d); |
588 | write_bank_n16k(a, d); |
7c6f7914 |
589 | write_bank_xor(a, d); |
e48f3f27 |
590 | } |
df6c895c |
591 | |
592 | Pico.ms.mapcnt ++; |
593 | if (Pico.ms.mapper) |
594 | elprintf(EL_STATUS, "autodetected %s mapper",mappers[Pico.ms.mapper]); |
f9ea940f |
595 | break; |
596 | } |
3e49ffd0 |
597 | } |
598 | |
c9076db9 |
599 | // Try to detect some tricky cases by their TMR header |
600 | // NB Codemasters, some Betas, most unlicensed games have no or invalid TMRs. |
601 | // if the cksum header is valid mark this by 0x.fff.... and use that instead |
602 | |
96948bdf |
603 | // TMR product codes and hardware type for known 50Hz-only games |
c9076db9 |
604 | static u32 region_pal[] = { // cf Meka, meka/meka.nam |
afdc2ed4 |
605 | 0x40207067 /* Addams Family */, 0x40207020 /* Back.Future 3 */, |
606 | 0x40207058 /* Battlemaniacs */, 0x40007105 /* Cal.Games 2 */, |
67c9ba38 |
607 | 0x402f7065 /* Dracula */ , 0x40007109 /* Home Alone */, |
afdc2ed4 |
608 | 0x40009024 /* Pwr.Strike 2 */ , 0x40207047 /* Predator 2 EU */, |
609 | 0x40002519 /* Quest.Yak */ , 0x40207064 /* Robocop 3 */, |
67c9ba38 |
610 | 0x4f205014 /* Sens.Soccer */ , 0x40002573 /* Sonic Blast */, |
afdc2ed4 |
611 | 0x40007080 /* S.Harrier EU */ , 0x40007038 /* Taito Chase */, |
67c9ba38 |
612 | 0x40009015 /* Sonic 2 EU */ , /* NBA Jam: no valid id/cksum */ |
c9076db9 |
613 | 0x4fff8872 /* Excell.Dizzy */ , 0x4ffffac4 /* Fantast.Dizzy */, |
614 | 0x4fff4a89 /* Csm.Spacehead */, 0x4fffe352 /* Micr.Machines */, |
67c9ba38 |
615 | 0x4fffa203 /* Bad Apple */ |
c9076db9 |
616 | }; |
617 | |
618 | // TMR product codes and hardware type for known non-FM games |
619 | static u32 no_fmsound[] = { // cf Meka, meka/meka.pat |
67c9ba38 |
620 | 0x40002070 /* Walter Payton */, 0x40017020 /* American Pro */, |
c9076db9 |
621 | 0x4fffe890 /* Wanted */ |
afdc2ed4 |
622 | }; |
623 | |
c9076db9 |
624 | // TMR product codes and hardware type for known GG carts running in SMS mode |
625 | // NB GG carts having the system type set to 4 (eg. HTH games) run as SMS anyway |
626 | static u32 gg_smsmode[] = { // cf https://www.smspower.org/Tags/SMS-GG |
67c9ba38 |
627 | 0x60002401 /* Castl.Ilusion */, 0x6f101018 /* Taito Chase */, |
40172b93 |
628 | 0x70709018 /* Olympic Gold */ , 0x70709038 /* Outrun EU */, |
ca206ba1 |
629 | 0x60801068 /* Predator 2 */ , 0x70408098 /* Prince.Persia */, |
67c9ba38 |
630 | 0x50101037 /* Rastan Saga */ , 0x7f086018 /* RC Grandprix */, |
ca206ba1 |
631 | 0x60002415 /* Super Kickoff */, 0x60801108 /* WWF.Steelcage */, |
c9076db9 |
632 | /* Excell.Dizzy, Fantast.Dizzy, Super Tetris: no valid id/cksum in TMR */ |
67c9ba38 |
633 | 0x4f813028 /* Tesserae */ |
ca206ba1 |
634 | }; |
635 | |
2ec9bec5 |
636 | void PicoResetMS(void) |
3e49ffd0 |
637 | { |
f9ea940f |
638 | unsigned tmr; |
c9076db9 |
639 | u32 id, hw, ck, i; |
032c76a3 |
640 | |
f9ea940f |
641 | // set preselected hw/mapper from config |
280bfc3c |
642 | if (PicoIn.hwSelect) { |
0aa63fce |
643 | PicoIn.AHW &= ~(PAHW_GG|PAHW_SG|PAHW_SC); |
280bfc3c |
644 | switch (PicoIn.hwSelect) { |
0aa63fce |
645 | case PHWS_GG: PicoIn.AHW |= PAHW_GG; break; |
646 | case PHWS_SG: PicoIn.AHW |= PAHW_SG; break; |
647 | case PHWS_SC: PicoIn.AHW |= PAHW_SC; break; |
280bfc3c |
648 | } |
f9ea940f |
649 | } |
df6c895c |
650 | Pico.ms.mapcnt = Pico.ms.mapper = 0; |
f9ea940f |
651 | if (PicoIn.mapper) |
652 | Pico.ms.mapper = PicoIn.mapper; |
3611781e |
653 | Pico.m.hardware |= PMS_HW_JAP; // default region Japan if no TMR header |
e48f3f27 |
654 | if (PicoIn.regionOverride > 2) |
655 | Pico.m.hardware &= ~PMS_HW_JAP; |
c9076db9 |
656 | Pico.m.hardware |= PMS_HW_FM; |
657 | if (!(PicoIn.opt & POPT_EN_YM2413)) |
658 | Pico.m.hardware &= ~PMS_HW_FM; |
f9ea940f |
659 | |
660 | // check if the ROM header contains more system information |
661 | for (tmr = 0x2000; tmr < 0xbfff && tmr <= Pico.romsize; tmr *= 2) { |
662 | if (!memcmp(Pico.rom + tmr-16, "TMR SEGA", 8)) { |
663 | hw = Pico.rom[tmr-1] >> 4; |
67c9ba38 |
664 | id = CPU_LE4(*(u32 *)&Pico.rom[tmr-4]); |
665 | ck = *(u16 *)&Pico.rom[tmr-6] | (id&0xf0000000) | 0xfff0000; |
666 | |
40172b93 |
667 | if (!PicoIn.hwSelect && !PicoIn.AHW && hw && ((id+1)&0xfffe) != 0) { |
55615f9c |
668 | if (hw >= 0x5 && hw < 0x8) |
0aa63fce |
669 | PicoIn.AHW |= PAHW_GG; // GG cartridge detected |
55615f9c |
670 | } |
671 | if (!PicoIn.regionOverride) { |
3611781e |
672 | Pico.m.hardware &= ~PMS_HW_JAP; |
55615f9c |
673 | if (hw == 0x5 || hw == 0x3) |
3611781e |
674 | Pico.m.hardware |= PMS_HW_JAP; // region Japan |
55615f9c |
675 | } |
afdc2ed4 |
676 | for (i = 0; i < sizeof(region_pal)/sizeof(*region_pal); i++) |
c9076db9 |
677 | if ((id == region_pal[i] || ck == region_pal[i]) && !PicoIn.regionOverride) |
678 | { |
afdc2ed4 |
679 | Pico.m.pal = 1; // requires 50Hz timing |
680 | break; |
681 | } |
ca206ba1 |
682 | for (i = 0; i < sizeof(gg_smsmode)/sizeof(*gg_smsmode); i++) |
c9076db9 |
683 | if ((id == gg_smsmode[i] || ck == gg_smsmode[i]) && !PicoIn.hwSelect) { |
ca206ba1 |
684 | PicoIn.AHW &= ~PAHW_GG; // requires SMS mode |
67c9ba38 |
685 | if (hw < 0x5) PicoIn.AHW |= PAHW_GG; |
ca206ba1 |
686 | break; |
687 | } |
c9076db9 |
688 | for (i = 0; i < sizeof(no_fmsound)/sizeof(*no_fmsound); i++) |
689 | if ((id == no_fmsound[i] || ck == no_fmsound[i])) { |
690 | Pico.m.hardware &= ~PMS_HW_FM; // incompatible with FM |
691 | break; |
692 | } |
f9ea940f |
693 | break; |
280bfc3c |
694 | } |
695 | } |
696 | |
f9ea940f |
697 | z80_reset(); |
698 | PsndReset(); // pal must be known here |
4af69a1d |
699 | |
700 | Pico.ms.io_ctl = (PicoIn.AHW & (PAHW_SG|PAHW_SC)) ? 0xf5 : 0xff; |
f9ea940f |
701 | Pico.ms.fm_ctl = 0xff; |
f9ea940f |
702 | |
032c76a3 |
703 | // reset memory mapping |
704 | PicoMemSetupMS(); |
1c70532f |
705 | |
1bb3f2cc |
706 | // BIOS, VDP intialisation |
1c70532f |
707 | Pico.video.reg[0] = 0x36; |
708 | Pico.video.reg[1] = 0xa0; |
709 | Pico.video.reg[2] = 0xff; |
710 | Pico.video.reg[3] = 0xff; |
711 | Pico.video.reg[4] = 0xff; |
712 | Pico.video.reg[5] = 0xff; |
713 | Pico.video.reg[6] = 0xfb; |
714 | Pico.video.reg[7] = 0x00; |
715 | Pico.video.reg[8] = 0x00; |
716 | Pico.video.reg[9] = 0x00; |
717 | Pico.video.reg[10] = 0xff; |
4af69a1d |
718 | Pico.m.dirtyPal = 1; |
1bb3f2cc |
719 | |
f9ea940f |
720 | // BIOS, clear zram (unitialized on Mark-III, cf src/mame/drivers/sms.cpp) |
0aa63fce |
721 | i = !(PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_JAP) ? 0xf0 : 0x00; |
3611781e |
722 | memset(PicoMem.zram, i, sizeof(PicoMem.zram)); |
3e49ffd0 |
723 | } |
724 | |
725 | void PicoPowerMS(void) |
726 | { |
87b0845f |
727 | int s, tmp; |
728 | |
88fd63ad |
729 | memset(&PicoMem,0,sizeof(PicoMem)); |
2ec9bec5 |
730 | memset(&Pico.video,0,sizeof(Pico.video)); |
731 | memset(&Pico.m,0,sizeof(Pico.m)); |
2ec9bec5 |
732 | |
87b0845f |
733 | // calculate a mask for bank writes. |
734 | // ROM loader has aligned the size for us, so this is safe. |
735 | s = 0; tmp = Pico.romsize; |
736 | while ((tmp >>= 1) != 0) |
737 | s++; |
738 | if (Pico.romsize > (1 << s)) |
739 | s++; |
740 | tmp = 1 << s; |
741 | bank_mask = (tmp - 1) >> 14; |
742 | |
1cdc3f84 |
743 | PicoMem.ioports[0] = 0xc3; // hack to jump @0 at end of RAM to wrap around |
f9ea940f |
744 | Pico.ms.mapper = PicoIn.mapper; |
2ec9bec5 |
745 | PicoReset(); |
3e49ffd0 |
746 | } |
747 | |
748 | void PicoMemSetupMS(void) |
749 | { |
7c16d135 |
750 | u8 mapper = Pico.ms.mapper; |
b7209504 |
751 | int sz = (/*PicoIn.AHW & (PAHW_SG|PAHW_SC) ? 2 :*/ 8) * 1024; |
0aa63fce |
752 | u32 a; |
7c16d135 |
753 | |
0aa63fce |
754 | // RAM and its mirrors |
755 | for (a = 0xc000; a < 0x10000; a += sz) { |
756 | z80_map_set(z80_read_map, a, a + sz-1, PicoMem.zram, 0); |
757 | z80_map_set(z80_write_map, a, a + sz-1, PicoMem.zram, 0); |
758 | } |
b7209504 |
759 | a = 0xffff - (1<<Z80_MEM_SHIFT); |
760 | z80_map_set(z80_write_map, a+1, 0xffff, xwrite, 1); // mapper detection |
3e49ffd0 |
761 | |
0aa63fce |
762 | // ROM |
763 | z80_map_set(z80_read_map, 0x0000, 0xbfff, Pico.rom, 0); |
764 | z80_map_set(z80_write_map, 0x0000, 0xbfff, xwrite, 1); // mapper detection |
1ce2b092 |
765 | |
f9ea940f |
766 | // Nemesis mapper maps last 8KB rom bank #15 to adress 0 |
7c16d135 |
767 | if (mapper == PMS_MAP_NEMESIS && Pico.romsize > 0x1e000) |
f9ea940f |
768 | z80_map_set(z80_read_map, 0x0000, 0x1fff, Pico.rom + 0x1e000, 0); |
3e49ffd0 |
769 | #ifdef _USE_DRZ80 |
770 | drZ80.z80_in = z80_sms_in; |
771 | drZ80.z80_out = z80_sms_out; |
772 | #endif |
773 | #ifdef _USE_CZ80 |
3e49ffd0 |
774 | Cz80_Set_INPort(&CZ80, z80_sms_in); |
775 | Cz80_Set_OUTPort(&CZ80, z80_sms_out); |
776 | #endif |
032c76a3 |
777 | |
1cdc3f84 |
778 | // memory mapper setup, linear mapping of 1st 48KB |
7c16d135 |
779 | memset(Pico.ms.carthw, 0, sizeof(Pico.ms.carthw)); |
f9ea940f |
780 | if (mapper == PMS_MAP_MSX || mapper == PMS_MAP_NEMESIS) { |
1cdc3f84 |
781 | xwrite(0x0000, 4); |
782 | xwrite(0x0001, 5); |
783 | xwrite(0x0002, 2); |
784 | xwrite(0x0003, 3); |
7c16d135 |
785 | } else if (mapper == PMS_MAP_KOREA) { |
786 | xwrite(0xa000, 2); |
f9ea940f |
787 | } else if (mapper == PMS_MAP_N32K) { |
1bb3f2cc |
788 | xwrite(0xffff, 0); |
f9ea940f |
789 | } else if (mapper == PMS_MAP_N16K) { |
790 | xwrite(0x3ffe, 0); |
791 | xwrite(0x7fff, 1); |
792 | xwrite(0xbfff, 2); |
793 | } else if (mapper == PMS_MAP_JANGGUN) { |
794 | xwrite(0xfffe, 1); |
795 | xwrite(0xffff, 2); |
7c6f7914 |
796 | } else if (mapper == PMS_MAP_XOR) { |
797 | xwrite(0x2000, 0); |
f9ea940f |
798 | } else if (mapper == PMS_MAP_CODEM) { |
1cdc3f84 |
799 | xwrite(0x0000, 0); |
800 | xwrite(0x4000, 1); |
801 | xwrite(0x8000, 2); |
7c6f7914 |
802 | } else if (mapper == PMS_MAP_SEGA) { |
8ba60e31 |
803 | xwrite(0xfffc, 0); |
804 | xwrite(0xfffd, 0); |
805 | xwrite(0xfffe, 1); |
806 | xwrite(0xffff, 2); |
7c6f7914 |
807 | } else if (mapper == PMS_MAP_AUTO) { |
808 | // pre-initialize Sega mapper to linear mapping (else state load may fail) |
809 | Pico.ms.carthw[0xe] = 0x1; |
810 | Pico.ms.carthw[0xf] = 0x2; |
8ba60e31 |
811 | } |
3e49ffd0 |
812 | } |
813 | |
b4db550e |
814 | void PicoStateLoadedMS(void) |
815 | { |
8ba60e31 |
816 | u8 mapper = Pico.ms.mapper; |
da414888 |
817 | u8 zram_dff0[16]; // TODO xwrite also writes to zram :-/ |
818 | |
819 | memcpy(zram_dff0, PicoMem.zram+0x1ff0, 16); |
cab84f29 |
820 | if (mapper == PMS_MAP_8KBRAM || mapper == PMS_MAP_32KBRAM) { |
df6c895c |
821 | u16 a = Pico.ms.carthw[0] << 12; |
7c16d135 |
822 | xwrite(a, *(unsigned char *)(PicoMem.vram+0x4000)); |
823 | } else if (mapper == PMS_MAP_MSX || mapper == PMS_MAP_NEMESIS) { |
032c76a3 |
824 | xwrite(0x0000, Pico.ms.carthw[0]); |
825 | xwrite(0x0001, Pico.ms.carthw[1]); |
826 | xwrite(0x0002, Pico.ms.carthw[2]); |
827 | xwrite(0x0003, Pico.ms.carthw[3]); |
7c16d135 |
828 | } else if (mapper == PMS_MAP_KOREA) { |
829 | xwrite(0xa000, Pico.ms.carthw[0x0f]); |
830 | } else if (mapper == PMS_MAP_N32K) { |
1bb3f2cc |
831 | xwrite(0xffff, Pico.ms.carthw[0x0f]); |
7c16d135 |
832 | } else if (mapper == PMS_MAP_N16K) { |
f9ea940f |
833 | xwrite(0x3ffe, Pico.ms.carthw[0]); |
834 | xwrite(0x7fff, Pico.ms.carthw[1]); |
835 | xwrite(0xbfff, Pico.ms.carthw[2]); |
7c16d135 |
836 | } else if (mapper == PMS_MAP_JANGGUN) { |
f9ea940f |
837 | xwrite(0x4000, Pico.ms.carthw[2]); |
838 | xwrite(0x6000, Pico.ms.carthw[3]); |
839 | xwrite(0x8000, Pico.ms.carthw[4]); |
840 | xwrite(0xa000, Pico.ms.carthw[5]); |
7c6f7914 |
841 | } else if (mapper == PMS_MAP_XOR) { |
842 | xwrite(0x2000, Pico.ms.carthw[0]); |
7c16d135 |
843 | } else if (mapper == PMS_MAP_CODEM) { |
1cdc3f84 |
844 | xwrite(0x0000, Pico.ms.carthw[0]); |
845 | xwrite(0x4000, Pico.ms.carthw[1]); |
846 | xwrite(0x8000, Pico.ms.carthw[2]); |
7c16d135 |
847 | } else if (mapper == PMS_MAP_SEGA) { |
032c76a3 |
848 | xwrite(0xfffc, Pico.ms.carthw[0x0c]); |
849 | xwrite(0xfffd, Pico.ms.carthw[0x0d]); |
850 | xwrite(0xfffe, Pico.ms.carthw[0x0e]); |
851 | xwrite(0xffff, Pico.ms.carthw[0x0f]); |
852 | } |
da414888 |
853 | memcpy(PicoMem.zram+0x1ff0, zram_dff0, 16); |
b4db550e |
854 | } |
855 | |
3e49ffd0 |
856 | void PicoFrameMS(void) |
857 | { |
2ec9bec5 |
858 | struct PicoVideo *pv = &Pico.video; |
859 | int is_pal = Pico.m.pal; |
860 | int lines = is_pal ? 313 : 262; |
c644ce99 |
861 | int cycles_line = 228; |
93f9619e |
862 | int skip = PicoIn.skipFrame; |
2ec9bec5 |
863 | int lines_vis = 192; |
87b0845f |
864 | int hint; // Hint counter |
835122bc |
865 | int nmi; |
2ec9bec5 |
866 | int y; |
867 | |
4f2cdbf5 |
868 | PsndStartFrame(); |
869 | |
d5908845 |
870 | // for SMS the pause button generates an NMI, for GG ths is not the case |
93f9619e |
871 | nmi = (PicoIn.pad[0] >> 7) & 1; |
0aa63fce |
872 | if (!(PicoIn.AHW & PAHW_GG) && !Pico.ms.nmi_state && nmi) |
835122bc |
873 | z80_nmi(); |
874 | Pico.ms.nmi_state = nmi; |
875 | |
4b3e9d92 |
876 | if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) |
877 | lines_vis = (pv->reg[1] & 0x08) ? 240 : 224; |
0df7401c |
878 | PicoFrameStartSMS(); |
87b0845f |
879 | hint = pv->reg[0x0a]; |
200772b7 |
880 | |
1ce2b092 |
881 | // SMS: xscroll:f3 sprovr,vint, vcount:fc, hint:fd |
882 | // GG: xscroll:f5 sprovr,vint:fd vcount:fe, hint:ff |
2ec9bec5 |
883 | for (y = 0; y < lines; y++) |
884 | { |
1ce2b092 |
885 | Pico.t.z80c_line_start = Pico.t.z80c_aim; |
886 | |
887 | // advance the line counter. It is set back at some point in the VBLANK so |
888 | // that the line count in the active area (-32..lines+1) is contiguous. |
889 | pv->v_counter = Pico.m.scanline = (u8)y; |
7eeb85be |
890 | switch (is_pal ? -lines_vis : lines_vis) { |
891 | case 192: if (y > 218) pv->v_counter = y - (lines-256); break; |
892 | case 224: if (y > 234) pv->v_counter = y - (lines-256); break; |
67c9ba38 |
893 | /* case 240: if (y > 242) pv->v_counter = y - (lines-256); break; ? */ |
7eeb85be |
894 | case -192: if (y > 242) pv->v_counter = y - (lines-256); break; |
895 | case -224: if (y > 258) pv->v_counter = y - (lines-256); break; |
896 | case -240: if (y > 266) pv->v_counter = y - (lines-256); break; |
897 | } |
2ec9bec5 |
898 | |
1ce2b092 |
899 | // Parse sprites for the next line |
900 | if (y < lines_vis) |
901 | PicoParseSATSMS(y-1); |
902 | else if (y > lines-32) |
903 | PicoParseSATSMS(y-1-lines); |
904 | |
905 | // render next line |
19954be1 |
906 | if (y < lines_vis && !skip) |
0df7401c |
907 | PicoLineSMS(y); |
87b0845f |
908 | |
1ce2b092 |
909 | // take over status bits from previously rendered line TODO: cycle exact? |
910 | pv->status |= sprites_status; |
911 | sprites_status = 0; |
7eeb85be |
912 | |
985bbf1c |
913 | // Interrupt handling. Simulate interrupt flagged and immediately reset in |
914 | // same insn by flagging the irq, execute for 1 insn, then checking if the |
915 | // irq is still pending. (GG Chicago, SMS Back to the Future III) |
1ce2b092 |
916 | pv->pending_ints &= ~2; // lost if not caught in the same line |
87b0845f |
917 | if (y <= lines_vis) |
918 | { |
919 | if (--hint < 0) |
920 | { |
921 | hint = pv->reg[0x0a]; |
922 | pv->pending_ints |= 2; |
7eeb85be |
923 | z80_exec(Pico.t.z80c_cnt + 1); |
924 | |
985bbf1c |
925 | if ((pv->reg[0] & 0x10) && (pv->pending_ints & 2)) { |
87b0845f |
926 | elprintf(EL_INTS, "hint"); |
cf83610b |
927 | z80_int_assert(1); |
1bb3f2cc |
928 | } |
87b0845f |
929 | } |
930 | } |
200772b7 |
931 | else if (y == lines_vis + 1) { |
87b0845f |
932 | pv->pending_ints |= 1; |
7eeb85be |
933 | z80_exec(Pico.t.z80c_cnt + 1); |
985bbf1c |
934 | |
935 | if ((pv->reg[1] & 0x20) && (pv->pending_ints & 1)) { |
2ec9bec5 |
936 | elprintf(EL_INTS, "vint"); |
cf83610b |
937 | z80_int_assert(1); |
985bbf1c |
938 | } |
2ec9bec5 |
939 | } |
940 | |
7eeb85be |
941 | z80_exec(Pico.t.z80c_line_start + cycles_line); |
2ec9bec5 |
942 | } |
943 | |
274dd51a |
944 | z80_resetCycles(); |
9f1d5acd |
945 | PsndGetSamplesMS(lines); |
3e49ffd0 |
946 | } |
947 | |
87b0845f |
948 | void PicoFrameDrawOnlyMS(void) |
949 | { |
1ce2b092 |
950 | struct PicoVideo *pv = &Pico.video; |
87b0845f |
951 | int lines_vis = 192; |
952 | int y; |
953 | |
1ce2b092 |
954 | if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) |
955 | lines_vis = (pv->reg[1] & 0x08) ? 240 : 224; |
0df7401c |
956 | PicoFrameStartSMS(); |
87b0845f |
957 | |
1ce2b092 |
958 | for (y = 0; y < lines_vis; y++) { |
959 | PicoParseSATSMS(y-1); |
0df7401c |
960 | PicoLineSMS(y); |
1ce2b092 |
961 | } |
87b0845f |
962 | } |
963 | |
075672bf |
964 | // vim:ts=2:sw=2:expandtab |