cff531af |
1 | /* |
2 | * SMS emulation |
3 | * (C) notaz, 2009-2010 |
576c1b8a |
4 | * (C) irixxxx, 2021-2025 |
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 | |
92064e2f |
20 | #include <platform/common/input_pico.h> // for keyboard handling |
21 | |
22 | |
a2f24bfa |
23 | extern void YM2413_regWrite(unsigned reg); |
24 | extern void YM2413_dataWrite(unsigned data); |
25 | |
1ce2b092 |
26 | extern unsigned sprites_status; // TODO put in some hdr file! |
4c10007b |
27 | extern int sprites_zoom, xscroll; |
a2f24bfa |
28 | |
2ec9bec5 |
29 | static unsigned char vdp_data_read(void) |
3e49ffd0 |
30 | { |
2ec9bec5 |
31 | struct PicoVideo *pv = &Pico.video; |
32 | unsigned char d; |
33 | |
1c120817 |
34 | d = Pico.ms.vdp_buffer; |
35 | Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)]; |
2ec9bec5 |
36 | pv->addr = (pv->addr + 1) & 0x3fff; |
37 | pv->pending = 0; |
38 | return d; |
39 | } |
40 | |
41 | static unsigned char vdp_ctl_read(void) |
42 | { |
1c25c32c |
43 | struct PicoVideo *pv = &Pico.video; |
44 | unsigned char d; |
45 | |
cf83610b |
46 | z80_int_assert(0); |
1c25c32c |
47 | d = pv->status | (pv->pending_ints << 7); |
48 | pv->pending = pv->pending_ints = 0; |
49 | pv->status = 0; |
2ec9bec5 |
50 | |
1c120817 |
51 | if (pv->reg[0] & 0x04) |
52 | d |= 0x1f; // unused bits in mode 4 read as 1 |
53 | |
2ec9bec5 |
54 | elprintf(EL_SR, "VDP sr: %02x", d); |
55 | return d; |
56 | } |
57 | |
58 | static void vdp_data_write(unsigned char d) |
59 | { |
60 | struct PicoVideo *pv = &Pico.video; |
61 | |
62 | if (pv->type == 3) { |
ace18401 |
63 | // cram. 32 on SMS, but 64 on MD. Fill 2nd half of cram for prio bit mirror |
0aa63fce |
64 | if (PicoIn.AHW & PAHW_GG) { // GG, same layout as MD |
466fa079 |
65 | unsigned a = pv->addr & 0x3f; |
e4da4fe8 |
66 | if (a & 0x1) { // write complete color on high byte write |
1c120817 |
67 | u16 c = ((d&0x0f) << 8) | Pico.ms.vdp_buffer; |
e4da4fe8 |
68 | if (PicoMem.cram[a >> 1] != c) Pico.m.dirtyPal = 1; |
ace18401 |
69 | PicoMem.cram[a >> 1] = PicoMem.cram[(a >> 1)+0x20] = c; |
e4da4fe8 |
70 | } |
466fa079 |
71 | } else { // SMS, convert to MD layout (00BbGgRr to 0000BbBbGgGgRrRr) |
72 | unsigned a = pv->addr & 0x1f; |
e4da4fe8 |
73 | u16 c = ((d&0x30)<<6) + ((d&0x0c)<<4) + ((d&0x03)<<2); |
466fa079 |
74 | if (PicoMem.cram[a] != (c | (c>>2))) Pico.m.dirtyPal = 1; |
ace18401 |
75 | PicoMem.cram[a] = PicoMem.cram[a+0x20] = c | (c>>2); |
466fa079 |
76 | } |
2ec9bec5 |
77 | } else { |
57c5a5e5 |
78 | PicoMem.vramb[MEM_LE2(pv->addr)] = d; |
2ec9bec5 |
79 | } |
80 | pv->addr = (pv->addr + 1) & 0x3fff; |
2ec9bec5 |
81 | |
1c120817 |
82 | Pico.ms.vdp_buffer = d; |
2ec9bec5 |
83 | pv->pending = 0; |
84 | } |
85 | |
2db0d004 |
86 | // VDP horizontal timing, total 342 px: |
87 | // 256 px active display, |
88 | // 23 px right border+blanking, |
89 | // 26 px hsync, |
90 | // 37 px left blanking+border |
4c10007b |
91 | // VINT is at the beginning of hsync, and HINT is one px later. Relative TO V/HINT: |
92 | // -18..-2 px 1st half of sprite attribute table (r5) scan |
93 | // -10 px sprite mode latching (r1,r0) |
94 | // -2 px hscroll latching (r8) |
95 | // hscroll is probably latched internally due to it depending on the horizontal |
96 | // scroll lock, which has this at 0 for the top 16 lines. |
97 | // I don't think the sprite mode is really latched. The SAT scan determines the |
98 | // relative y position within the sprite pattern, which will break since SAT |
99 | // scanning is done in one go here, while in reality it is distributed over |
100 | // several slots. Cache it here to avoid backward effects of later changes to r1 |
2db0d004 |
101 | // TODO: off by 2 CPU cycles according to VDPTEST? |
102 | |
cf83610b |
103 | static NOINLINE void vdp_reg_write(struct PicoVideo *pv, u8 a, u8 d) |
104 | { |
105 | int l; |
106 | |
107 | pv->reg[a] = d; |
108 | switch (a) { |
2db0d004 |
109 | case 0: // mode control 1 |
cf83610b |
110 | l = pv->pending_ints & (d >> 3) & 2; |
111 | elprintf(EL_INTS, "hint %d", l); |
112 | z80_int_assert(l); |
4c10007b |
113 | if (z80_cyclesDone() - Pico.t.z80c_line_start < 228 - (int)(10*1.5)+2) |
2db0d004 |
114 | sprites_zoom = (pv->reg[1] & 0x3) | (pv->reg[0] & 0x8); |
cf83610b |
115 | break; |
2db0d004 |
116 | case 1: // mode control 2 |
cf83610b |
117 | l = pv->pending_ints & (d >> 5) & 1; |
118 | elprintf(EL_INTS, "vint %d", l); |
119 | z80_int_assert(l); |
2db0d004 |
120 | if (z80_cyclesDone() - Pico.t.z80c_line_start < 228 - (int)(10*1.5)+2) |
121 | sprites_zoom = (pv->reg[1] & 0x3) | (pv->reg[0] & 0x8); |
122 | break; |
2db0d004 |
123 | case 8: // horizontal scroll |
124 | if (z80_cyclesDone() - Pico.t.z80c_line_start < 228 - (int)(2*1.5)+2) |
125 | xscroll = d; |
cf83610b |
126 | break; |
127 | } |
128 | } |
129 | |
130 | static void vdp_ctl_write(u8 d) |
2ec9bec5 |
131 | { |
132 | struct PicoVideo *pv = &Pico.video; |
133 | |
134 | if (pv->pending) { |
e4da4fe8 |
135 | pv->type = d >> 6; |
136 | if (pv->type == 2) { |
200772b7 |
137 | elprintf(EL_IO, " VDP r%02x=%02x", d & 0x0f, pv->addr & 0xff); |
cf83610b |
138 | if (pv->reg[d & 0x0f] != (u8)pv->addr) |
139 | vdp_reg_write(pv, d & 0x0f, pv->addr); |
2ec9bec5 |
140 | } |
2ec9bec5 |
141 | pv->addr &= 0x00ff; |
142 | pv->addr |= (d & 0x3f) << 8; |
e4da4fe8 |
143 | if (pv->type == 0) { |
1c120817 |
144 | Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)]; |
e4da4fe8 |
145 | pv->addr = (pv->addr + 1) & 0x3fff; |
146 | } |
2ec9bec5 |
147 | } else { |
148 | pv->addr &= 0x3f00; |
149 | pv->addr |= d; |
150 | } |
151 | pv->pending ^= 1; |
3e49ffd0 |
152 | } |
153 | |
1cdc3f84 |
154 | static u8 vdp_hcounter(int cycles) |
155 | { |
156 | // 171 slots per scanline of 228 clocks, counted 0xf4-0x93, 0xe9-0xf3 |
157 | // this matches h counter tables in SMSVDPTest: |
158 | // hc = (cycles+2) * 171 /228 -1 + 0xf4; |
159 | int hc = (((cycles+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing |
160 | if (hc > 0x193) hc += 0xe9-0x93-1; |
161 | return hc; |
162 | } |
163 | |
438abd99 |
164 | // keyboard matrix: |
165 | // port A @0xdc: |
166 | // row 0: 1 2 3 4 5 6 7 |
167 | // row 1: q w e r t y u |
168 | // row 2: a s d f g h j |
169 | // row 3: z x c v b n m |
20c9a3ba |
170 | // row 4: ED SP CL DL kana space clear/home del/ins |
171 | // row 5: , . / PI v < > pi |
172 | // row 6: k l ; : ] CR ^ enter |
438abd99 |
173 | // row 7: i o p @ [ |
174 | // port B @0xdd: |
20c9a3ba |
175 | // row 8: 8 9 0 - ^ YN BR yen break |
176 | // row 9: GR graph |
177 | // row 10: CTL control |
178 | // row 11: FN SFT func shift |
438abd99 |
179 | static unsigned char kbd_matrix[12]; |
180 | |
181 | // row | col |
182 | static unsigned char kbd_map[] = { |
20c9a3ba |
183 | [PEVB_KBD_1] = 0x00, |
184 | [PEVB_KBD_2] = 0x01, |
185 | [PEVB_KBD_3] = 0x02, |
186 | [PEVB_KBD_4] = 0x03, |
187 | [PEVB_KBD_5] = 0x04, |
188 | [PEVB_KBD_6] = 0x05, |
189 | [PEVB_KBD_7] = 0x06, |
190 | [PEVB_KBD_8] = 0x80, |
191 | [PEVB_KBD_9] = 0x81, |
192 | [PEVB_KBD_0] = 0x82, |
193 | [PEVB_KBD_MINUS] = 0x83, |
194 | [PEVB_KBD_CARET] = 0x84, |
195 | [PEVB_KBD_YEN] = 0x85, |
196 | [PEVB_KBD_ESCAPE] = 0x86, // break |
438abd99 |
197 | |
20c9a3ba |
198 | [PEVB_KBD_q] = 0x10, |
199 | [PEVB_KBD_w] = 0x11, |
200 | [PEVB_KBD_e] = 0x12, |
201 | [PEVB_KBD_r] = 0x13, |
202 | [PEVB_KBD_t] = 0x14, |
203 | [PEVB_KBD_y] = 0x15, |
204 | [PEVB_KBD_u] = 0x16, |
205 | [PEVB_KBD_i] = 0x70, |
206 | [PEVB_KBD_o] = 0x71, |
207 | [PEVB_KBD_p] = 0x72, |
208 | [PEVB_KBD_AT] = 0x73, |
209 | [PEVB_KBD_LEFTBRACKET] = 0x74, |
438abd99 |
210 | |
20c9a3ba |
211 | [PEVB_KBD_a] = 0x20, |
212 | [PEVB_KBD_s] = 0x21, |
213 | [PEVB_KBD_d] = 0x22, |
214 | [PEVB_KBD_f] = 0x23, |
215 | [PEVB_KBD_g] = 0x24, |
216 | [PEVB_KBD_h] = 0x25, |
217 | [PEVB_KBD_j] = 0x26, |
218 | [PEVB_KBD_k] = 0x60, |
219 | [PEVB_KBD_l] = 0x61, |
220 | [PEVB_KBD_SEMICOLON] = 0x62, |
221 | [PEVB_KBD_COLON] = 0x63, |
222 | [PEVB_KBD_RIGHTBRACKET] = 0x64, |
223 | [PEVB_KBD_RETURN] = 0x65, |
224 | [PEVB_KBD_UP] = 0x66, // up |
438abd99 |
225 | |
20c9a3ba |
226 | [PEVB_KBD_z] = 0x30, |
227 | [PEVB_KBD_x] = 0x31, |
228 | [PEVB_KBD_c] = 0x32, |
229 | [PEVB_KBD_v] = 0x33, |
230 | [PEVB_KBD_b] = 0x34, |
231 | [PEVB_KBD_n] = 0x35, |
232 | [PEVB_KBD_m] = 0x36, |
233 | [PEVB_KBD_COMMA] = 0x50, |
234 | [PEVB_KBD_PERIOD] = 0x51, |
235 | [PEVB_KBD_SLASH] = 0x52, |
236 | [PEVB_KBD_RO] = 0x53, // pi |
237 | [PEVB_KBD_DOWN] = 0x54, // down |
238 | [PEVB_KBD_LEFT] = 0x55, // left |
239 | [PEVB_KBD_RIGHT] = 0x56, // right |
438abd99 |
240 | |
20c9a3ba |
241 | [PEVB_KBD_CJK] = 0x40, // kana |
242 | [PEVB_KBD_SPACE] = 0x41, // space |
243 | [PEVB_KBD_HOME] = 0x42, // clear/home |
244 | [PEVB_KBD_BACKSPACE] = 0x43, // del/ins |
438abd99 |
245 | |
20c9a3ba |
246 | [PEVB_KBD_SOUND] = 0x96, // graph |
247 | [PEVB_KBD_CAPSLOCK] = 0xa6, // ctrl |
248 | [PEVB_KBD_FUNC] = 0xb5, // func |
576c1b8a |
249 | [PEVB_KBD_LSHIFT] = 0xb6, // shift (both keys on the same column) |
250 | [PEVB_KBD_RSHIFT] = 0xb6, |
438abd99 |
251 | }; |
252 | |
253 | static void kbd_update(void) |
254 | { |
20c9a3ba |
255 | u32 key = (PicoIn.kbd & 0x00ff); |
256 | u32 sft = (PicoIn.kbd & 0xff00) >> 8; |
438abd99 |
257 | |
258 | memset(kbd_matrix, 0, sizeof(kbd_matrix)); |
92064e2f |
259 | if (sft) { |
438abd99 |
260 | int rc = kbd_map[sft]; |
261 | kbd_matrix[rc>>4] = (1 << (rc&0x7)); |
262 | } |
92064e2f |
263 | if (key) { |
438abd99 |
264 | int rc = kbd_map[key]; |
265 | kbd_matrix[rc>>4] = (1 << (rc&0x7)); |
266 | } |
267 | } |
268 | |
92064e2f |
269 | // tape handling |
270 | |
271 | static struct tape { |
272 | FILE *ftape; |
273 | int fsize; // size of sample in bytes |
274 | int mode; // "w", "r" |
275 | |
276 | int cycle; // latest polling cycle |
277 | int pause; // pause tape playing |
278 | int poll_cycles; // for auto play/pause detection |
279 | int poll_count; |
280 | |
281 | int phase; // start cycle of current sample |
282 | int cycles_sample; // cycles per sample |
283 | u32 cycles_mult; // Q32, inverse of cycles per sample |
284 | |
285 | int isbit; // is bitstream format? |
286 | u8 bitsample; // bitstream sample |
287 | s16 wavsample; // wave file sample |
288 | } tape; |
289 | |
290 | static u8 tape_update(int cycle) |
291 | { |
292 | struct tape *pt = &tape; |
293 | u8 ret = 0; |
294 | int phase = cycle - pt->phase; |
295 | int count = ((u64)phase * pt->cycles_mult) >> 32; |
296 | int cycles = cycle - pt->cycle; |
297 | |
298 | if (cycles < 0 || pt->ftape == NULL || pt->mode != 'r') return 0; |
299 | pt->cycle = cycle; |
300 | |
301 | // auto play/pause detection: |
302 | pt->poll_cycles += cycles; |
303 | if (pt->pause) { |
304 | // if in pause and poll cycles are short for 1/4s, play |
305 | if (cycles < OSC_NTSC/15/1000) { |
306 | if (pt->poll_cycles > OSC_NTSC/15/4) { |
307 | pt->pause = pt->poll_cycles = pt->poll_count = 0; |
308 | } |
309 | } else { |
310 | // long poll cycles reset the logic |
311 | pt->poll_cycles = 0; |
312 | } |
313 | } else { |
314 | // if in play and poll cycles are long for 1/4s, pause |
315 | if (cycles >= OSC_NTSC/15/1000) { |
316 | if (pt->poll_cycles > OSC_NTSC/15/4) { |
317 | pt->pause = 1; pt->poll_cycles = 0; |
318 | } |
319 | pt->poll_count = 0; |
320 | } else if (++pt->poll_count > 10) { |
321 | // >10 short poll cycles reset the logic. This is to cover for software |
322 | // polling the keyboard matrix, which is partly on PB too. |
323 | pt->poll_cycles = pt->poll_count = 0; |
324 | } |
325 | } |
326 | |
327 | if (pt->pause) { |
328 | pt->phase = cycle; |
329 | return 0; |
330 | } |
331 | |
332 | // skip samples if necessary |
333 | if (count > 1) { |
334 | fseek(pt->ftape, (count-1) * pt->fsize, SEEK_CUR); |
335 | pt->phase += (count-1) * pt->cycles_sample; |
336 | } |
337 | |
338 | // read a new sample from file if needed |
339 | if (count) { |
340 | if (pt->isbit) |
341 | fread(&pt->bitsample, 1, sizeof(u8), pt->ftape); |
342 | else { |
343 | // read sample only from 1st channel |
344 | fread(&pt->wavsample, 1, sizeof(u16), pt->ftape); |
345 | if (pt->fsize > sizeof(u16)) // skip other channels |
346 | fseek(pt->ftape, pt->fsize - sizeof(u16), SEEK_CUR); |
347 | #if ! CPU_IS_LE |
348 | pt->wavsample = (u8)(pt->wavsample >> 8) | (pt->wavsample << 8); |
349 | #endif |
350 | } |
351 | // catch EOF and reading errors |
352 | if (feof(pt->ftape) || ferror(pt->ftape)) { |
353 | fclose(pt->ftape); |
354 | pt->ftape = NULL; |
355 | } |
356 | pt->phase += pt->cycles_sample; |
357 | } |
358 | |
359 | // compute result from sample |
360 | if (pt->isbit) { |
361 | phase = cycle - pt->phase; // recompute as phase might have changed above. |
29455901 |
362 | if (pt->bitsample == '0') ret = ((phase * pt->cycles_mult)>>31) & 1; |
363 | if (pt->bitsample == '1') ret = ((phase * pt->cycles_mult)>>30) & 1; |
92064e2f |
364 | } else |
365 | ret = pt->wavsample >= 0x0800; // 1/16th of the max volume |
366 | |
367 | return ret; |
368 | } |
369 | |
370 | static void tape_write(int cycle, int data) |
371 | { |
372 | struct tape *pt = &tape; |
373 | int cycles = cycle - pt->cycle; // cycles since last write |
374 | int samples; |
375 | |
376 | if (cycles < 0 || pt->ftape == NULL || pt->mode != 'w') return; |
377 | pt->cycle = cycle; |
378 | pt->poll_cycles += cycles; |
379 | |
380 | // write samples to file. Stop if the sample doesn't change for more than 2s |
6d885935 |
381 | if (pt->isbit) { |
382 | pt->poll_count += (data >= 0); |
383 | if (data >= 0 && pt->poll_cycles >= pt->cycles_sample*15/16) { |
094b0c00 |
384 | // determine bit, duration ~1/1200s, either 2400Hz, or 1200Hz, or bust |
6d885935 |
385 | switch (pt->poll_count) { |
386 | case 4: pt->bitsample = '1'; break; // 2*2400Hz |
387 | case 2: pt->bitsample = '0'; break; // 1*1200Hz |
388 | default: pt->bitsample = ' '; break; // ignore everything else |
389 | } |
094b0c00 |
390 | if (pt->poll_cycles >= pt->cycles_sample*17/16) pt->bitsample = ' '; |
6d885935 |
391 | |
392 | if (pt->poll_cycles < OSC_NTSC/15*2) { |
393 | samples = ((u64)pt->poll_cycles * pt->cycles_mult + 0x80000000LL) >> 32; |
394 | while (samples-- > 0 && !ferror(pt->ftape)) |
395 | fwrite(&pt->bitsample, 1, sizeof(u8), pt->ftape); |
396 | } |
397 | |
398 | pt->poll_count = pt->poll_cycles = 0; |
399 | } |
400 | } else { |
401 | if (pt->wavsample && pt->poll_cycles < OSC_NTSC/15*2) { |
402 | samples = ((u64)cycles * pt->cycles_mult) >> 32; |
403 | while (samples-- > 0 && !ferror(pt->ftape)) |
404 | fwrite(&pt->wavsample, 1, sizeof(s16), pt->ftape); |
92064e2f |
405 | } |
92064e2f |
406 | |
6d885935 |
407 | // current sample value in little endian, for writing next time |
408 | if (data != -1) { |
409 | pt->wavsample = (data ? 0x7ff8 : 0x8008); |
92064e2f |
410 | #if ! CPU_IS_LE |
6d885935 |
411 | pt->wavsample = (u8)(pt->wavsample >> 8) | (pt->wavsample << 8); |
92064e2f |
412 | #endif |
6d885935 |
413 | pt->poll_cycles = 0; |
414 | } |
415 | } |
416 | |
417 | // catch write errors |
418 | if (ferror(pt->ftape)) { |
419 | fclose(pt->ftape); |
420 | pt->ftape = NULL; |
92064e2f |
421 | } |
422 | } |
423 | |
424 | // NB: SC-3000 has a 8255 chip, mapped to 0xdc-0xdf. Not fully emulated. |
425 | |
2ec9bec5 |
426 | static unsigned char z80_sms_in(unsigned short a) |
3e49ffd0 |
427 | { |
1c120817 |
428 | unsigned char d = 0xff; |
2ec9bec5 |
429 | |
1c120817 |
430 | a &= 0xff; |
200772b7 |
431 | elprintf(EL_IO, "z80 port %04x read", a); |
1c120817 |
432 | if(a >= 0xf0){ |
c9076db9 |
433 | if (Pico.m.hardware & PMS_HW_FM) { |
1c120817 |
434 | switch(a) |
81d54be1 |
435 | { |
436 | case 0xf0: |
437 | // FM reg port |
438 | break; |
439 | case 0xf1: |
440 | // FM data port |
441 | break; |
442 | case 0xf2: |
443 | // bit 0 = 1 active FM Pac |
1c120817 |
444 | d = 0xf8 | Pico.ms.fm_ctl; |
81d54be1 |
445 | break; |
a2f24bfa |
446 | } |
a2f24bfa |
447 | } |
448 | } |
449 | else{ |
1c120817 |
450 | switch (a & 0xc1) |
a2f24bfa |
451 | { |
452 | case 0x00: |
453 | case 0x01: |
0aa63fce |
454 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) { // GG I/O area |
1c120817 |
455 | switch (a) { |
2db0d004 |
456 | case 0: d = (~PicoIn.pad[0] & 0x80) | |
457 | (!(Pico.m.hardware & PMS_HW_JAP) << 6); break; |
1603058a |
458 | case 1: d = Pico.ms.io_gg[1] | (Pico.ms.io_gg[2] & 0x7f); break; |
459 | case 5: d = Pico.ms.io_gg[5] & 0xf8; break; |
460 | default: d = Pico.ms.io_gg[a]; break; |
1c120817 |
461 | } |
462 | } |
a2f24bfa |
463 | break; |
464 | |
465 | case 0x40: /* V counter */ |
466 | d = Pico.video.v_counter; |
467 | elprintf(EL_HVCNT, "V counter read: %02x", d); |
468 | break; |
469 | |
cc1547e8 |
470 | case 0x41: /* H counter */ |
1bb3f2cc |
471 | d = Pico.ms.vdp_hlatch; |
a2f24bfa |
472 | elprintf(EL_HVCNT, "H counter read: %02x", d); |
473 | break; |
474 | |
475 | case 0x80: |
476 | d = vdp_data_read(); |
477 | break; |
478 | |
479 | case 0x81: |
480 | d = vdp_ctl_read(); |
481 | break; |
482 | |
483 | case 0xc0: /* I/O port A and B */ |
92064e2f |
484 | // For SC-3000: 8255 port A, assume always configured for input |
4af69a1d |
485 | if (! (PicoIn.AHW & PAHW_SC) || (Pico.ms.io_sg & 7) == 7) { |
cab84f29 |
486 | d = ~((PicoIn.pad[0] & 0x3f) | (PicoIn.pad[1] << 6)); |
4af69a1d |
487 | if (!(Pico.ms.io_ctl & 0x01)) // TR as output |
488 | d = (d & ~0x20) | ((Pico.ms.io_ctl << 1) & 0x20); |
438abd99 |
489 | } else { |
490 | int i; // read kbd 8 bits |
491 | kbd_update(); |
492 | for (i = 7; i >= 0; i--) |
493 | d = (d<<1) | !(kbd_matrix[i] & (1<<(Pico.ms.io_sg&7))); |
494 | } |
a2f24bfa |
495 | break; |
496 | |
497 | case 0xc1: /* I/O port B and miscellaneous */ |
92064e2f |
498 | // For SC-3000: 8255 port B, assume always configured for input |
0aa63fce |
499 | if (! (PicoIn.AHW & PAHW_SC) || (Pico.ms.io_sg & 7) == 7) { |
cab84f29 |
500 | d = (Pico.ms.io_ctl & 0x80) | ((Pico.ms.io_ctl << 1) & 0x40) | 0x30; |
501 | d |= ~(PicoIn.pad[1] >> 2) & 0x0f; |
4af69a1d |
502 | if (!(Pico.ms.io_ctl & 0x04)) // TR as output |
503 | d = (d & ~0x08) | ((Pico.ms.io_ctl >> 3) & 0x08); |
cab84f29 |
504 | if (Pico.ms.io_ctl & 0x08) d |= 0x80; // TH as input is unconnected |
505 | if (Pico.ms.io_ctl & 0x02) d |= 0x40; |
438abd99 |
506 | } else { |
507 | int i; // read kbd 4 bits |
508 | kbd_update(); |
509 | for (i = 11; i >= 8; i--) |
510 | d = (d<<1) | !(kbd_matrix[i] & (1<<(Pico.ms.io_sg&7))); |
92064e2f |
511 | // bit 5 = printer fault |
512 | // bit 6 = printer busy |
513 | d &= ~0x60; // set so that BASIC thinks printer is connected |
514 | } |
515 | if (PicoIn.AHW & PAHW_SC) { |
516 | // bit 7 = tape input |
517 | d &= ~0x80; |
518 | d |= tape_update(z80_cyclesDone()) ? 0x80 : 0; |
438abd99 |
519 | } |
a2f24bfa |
520 | break; |
521 | } |
2ec9bec5 |
522 | } |
200772b7 |
523 | elprintf(EL_IO, "ret = %02x", d); |
2ec9bec5 |
524 | return d; |
525 | } |
526 | |
527 | static void z80_sms_out(unsigned short a, unsigned char d) |
528 | { |
200772b7 |
529 | elprintf(EL_IO, "z80 port %04x write %02x", a, d); |
2ec9bec5 |
530 | |
1c120817 |
531 | a &= 0xff; |
cab84f29 |
532 | if (a >= 0xf0){ |
c9076db9 |
533 | if (Pico.m.hardware & PMS_HW_FM) { |
1c120817 |
534 | switch(a) |
81d54be1 |
535 | { |
536 | case 0xf0: |
537 | // FM reg port |
43ef4071 |
538 | Pico.m.hardware |= PMS_HW_FMUSED; |
81d54be1 |
539 | YM2413_regWrite(d); |
81d54be1 |
540 | break; |
541 | case 0xf1: |
542 | // FM data port |
543 | YM2413_dataWrite(d); |
81d54be1 |
544 | break; |
545 | case 0xf2: |
546 | // bit 0 = 1 active FM Pac |
1c120817 |
547 | Pico.ms.fm_ctl = d & 0x1; |
81d54be1 |
548 | break; |
549 | } |
a2f24bfa |
550 | } |
551 | } |
cab84f29 |
552 | else { |
1c120817 |
553 | switch (a & 0xc1) |
a2f24bfa |
554 | { |
1c120817 |
555 | case 0x00: |
0aa63fce |
556 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) // GG I/O area |
1c120817 |
557 | Pico.ms.io_gg[a] = d; |
70efc52d |
558 | if ((PicoIn.AHW & PAHW_GG) && a == 0x6) |
559 | SN76496Config(d); |
1c120817 |
560 | break; |
a2f24bfa |
561 | case 0x01: |
0aa63fce |
562 | if ((PicoIn.AHW & PAHW_GG) && a < 0x8) { // GG I/O area |
1c120817 |
563 | Pico.ms.io_gg[a] = d; |
564 | } else { |
565 | // pad. latch hcounter if one of the TH lines is switched to 1 |
1cdc3f84 |
566 | if ((Pico.ms.io_ctl ^ d) & d & 0xa0) |
7eeb85be |
567 | Pico.ms.vdp_hlatch = vdp_hcounter(z80_cyclesDone() - Pico.t.z80c_line_start); |
1c120817 |
568 | Pico.ms.io_ctl = d; |
cc1547e8 |
569 | } |
a2f24bfa |
570 | break; |
571 | |
572 | case 0x40: |
573 | case 0x41: |
1bb3f2cc |
574 | PsndDoPSG(z80_cyclesDone()); |
a2f24bfa |
575 | SN76496Write(d); |
576 | break; |
577 | |
578 | case 0x80: |
579 | vdp_data_write(d); |
580 | break; |
581 | |
582 | case 0x81: |
583 | vdp_ctl_write(d); |
584 | break; |
cab84f29 |
585 | |
586 | case 0xc0: |
92064e2f |
587 | if ((PicoIn.AHW & PAHW_SC) && (a & 0x2)) { |
588 | // For SC-3000: 8255 port C, assume always configured for output |
576c1b8a |
589 | Pico.ms.io_sg = d; // 0xc2 = kbd/pad matrix column select |
92064e2f |
590 | // bit 4 = tape output |
591 | // bit 5 = printer data |
592 | // bit 6 = printer reset |
593 | // bit 7 = printer feed |
594 | } |
595 | break; |
596 | case 0xc1: |
597 | if ((PicoIn.AHW & PAHW_SC) && (a & 0x2) && !(d & 0x80)) { |
598 | // For SC-3000: 8255 control port. BSR mode used for printer and tape. |
87667793 |
599 | int b = (d>>1) & 0x7; |
600 | Pico.ms.io_sg &= ~(1<<b); |
601 | Pico.ms.io_sg |= ((d&1)<<b); |
602 | |
92064e2f |
603 | // debug hack to copy printer data to stdout. |
aeda6aba |
604 | // Printer data is sent at about 4.7 KBaud, 10 bits per character: |
92064e2f |
605 | // start=0, 8 data bits (LSB first), stop=1. data line is inverted. |
606 | // no Baud tracking needed as all bits are sent through here. |
607 | static int chr, bit; |
87667793 |
608 | if (b == 4) { // tape out |
92064e2f |
609 | tape_write(z80_cyclesDone(), d&1); |
87667793 |
610 | } else if (b == 5) { // !data |
92064e2f |
611 | if (!bit) { |
612 | if (d&1) // start bit |
613 | bit = 8; |
614 | } else { |
615 | chr = (chr>>1) | (d&1 ? 0 : 0x80); |
616 | if (!--bit) { |
617 | printf("%c",chr); |
618 | if (chr == 0xd) printf("\n"); |
619 | } |
620 | } |
87667793 |
621 | } else if (b == 6 && !(d&1)) // !reset |
92064e2f |
622 | bit = 0; |
623 | } |
624 | break; |
a2f24bfa |
625 | } |
2ec9bec5 |
626 | } |
627 | } |
628 | |
7eeb85be |
629 | static void z80_exec(int aim) |
630 | { |
631 | Pico.t.z80c_aim = aim; |
632 | Pico.t.z80c_cnt += z80_run(Pico.t.z80c_aim - Pico.t.z80c_cnt); |
633 | } |
634 | |
f9ea940f |
635 | |
636 | // ROM/SRAM bank mapping, see https://www.smspower.org/Development/Mappers |
637 | |
87b0845f |
638 | static int bank_mask; |
639 | |
b784d4a5 |
640 | static void xwrite(unsigned int a, unsigned char d); |
641 | |
f9ea940f |
642 | |
643 | // Sega mapper. Maps 3 banks 16KB each, with SRAM support |
644 | static void write_sram_sega(unsigned short a, unsigned char d) |
b784d4a5 |
645 | { |
646 | // SRAM is mapped in 2 16KB banks, selected by bit 2 in control reg |
647 | a &= 0x3fff; |
648 | a += ((Pico.ms.carthw[0x0c] & 0x04) >> 2) * 0x4000; |
649 | |
650 | Pico.sv.changed |= (Pico.sv.data[a] != d); |
651 | Pico.sv.data[a] = d; |
652 | } |
653 | |
032c76a3 |
654 | static void write_bank_sega(unsigned short a, unsigned char d) |
2ec9bec5 |
655 | { |
f9ea940f |
656 | if (a < 0xfff8) return; |
1cdc3f84 |
657 | // avoid mapper detection for RAM fill with 0 |
658 | if (Pico.ms.mapper != PMS_MAP_SEGA && (Pico.ms.mapper || d == 0)) return; |
f9ea940f |
659 | |
660 | elprintf(EL_Z80BNK, "bank sega %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
661 | Pico.ms.mapper = PMS_MAP_SEGA; |
4af69a1d |
662 | if (d == Pico.ms.carthw[a & 0x0f]) return; |
b784d4a5 |
663 | Pico.ms.carthw[a & 0x0f] = d; |
8ba60e31 |
664 | |
2ec9bec5 |
665 | switch (a & 0x0f) |
666 | { |
2ec9bec5 |
667 | case 0x0d: |
b784d4a5 |
668 | d &= bank_mask; |
669 | z80_map_set(z80_read_map, 0x0400, 0x3fff, Pico.rom+0x400 + (d << 14), 0); |
2ec9bec5 |
670 | break; |
671 | case 0x0e: |
87b0845f |
672 | d &= bank_mask; |
2ec9bec5 |
673 | z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0); |
2ec9bec5 |
674 | break; |
b784d4a5 |
675 | |
676 | case 0x0c: |
677 | if (d & ~0x8c) |
678 | elprintf(EL_STATUS|EL_ANOMALY, "%02x written to control reg!", d); |
679 | /*FALLTHROUGH*/ |
2ec9bec5 |
680 | case 0x0f: |
b784d4a5 |
681 | if (Pico.ms.carthw[0xc] & 0x08) { |
682 | d = (Pico.ms.carthw[0xc] & 0x04) >> 2; |
683 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.sv.data + d*0x4000, 0); |
f9ea940f |
684 | z80_map_set(z80_write_map, 0x8000, 0xbfff, write_sram_sega, 1); |
b784d4a5 |
685 | } else { |
686 | d = Pico.ms.carthw[0xf] & bank_mask; |
687 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); |
b784d4a5 |
688 | z80_map_set(z80_write_map, 0x8000, 0xbfff, xwrite, 1); |
689 | } |
2ec9bec5 |
690 | break; |
691 | } |
3e49ffd0 |
692 | } |
693 | |
25559e54 |
694 | // Codemasters mapper. Similar to Sega, but different addresses |
1cdc3f84 |
695 | static void write_bank_codem(unsigned short a, unsigned char d) |
696 | { |
f9ea940f |
697 | if (a >= 0xc000 || (a & 0x3fff)) return; // address is 0x0000, 0x4000, 0x8000? |
698 | // don't detect linear mapping to avoid confusing with MSX |
699 | if (Pico.ms.mapper != PMS_MAP_CODEM && (Pico.ms.mapper || (a>>14) == d)) return; |
700 | elprintf(EL_Z80BNK, "bank codem %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
701 | Pico.ms.mapper = PMS_MAP_CODEM; |
4af69a1d |
702 | if (Pico.ms.carthw[a>>14] == d) return; |
1cdc3f84 |
703 | Pico.ms.carthw[a>>14] = d; |
704 | |
705 | d &= bank_mask; |
706 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
25559e54 |
707 | if (Pico.ms.carthw[1] & 0x80) { |
708 | z80_map_set(z80_read_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0); |
709 | z80_map_set(z80_write_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0); |
710 | } else { |
711 | d = Pico.ms.carthw[2] & bank_mask; |
712 | z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + (d << 14)+0x2000, 0); |
713 | z80_map_set(z80_write_map, 0xa000, 0xbfff, xwrite, 1); |
714 | } |
1cdc3f84 |
715 | } |
716 | |
f9ea940f |
717 | // MSX mapper. 4 selectable 8KB banks at the top |
032c76a3 |
718 | static void write_bank_msx(unsigned short a, unsigned char d) |
719 | { |
67c9ba38 |
720 | if (a > 0x0003) return; |
f9ea940f |
721 | // don't detect linear mapping to avoid confusing with Codemasters |
df6c895c |
722 | if (Pico.ms.mapper != PMS_MAP_MSX && (Pico.ms.mapper || (a|d) == 0 || d >= 0x80)) return; |
f9ea940f |
723 | elprintf(EL_Z80BNK, "bank msx %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
724 | Pico.ms.mapper = PMS_MAP_MSX; |
032c76a3 |
725 | Pico.ms.carthw[a] = d; |
726 | |
727 | a = (a^2)*0x2000 + 0x4000; |
728 | d &= 2*bank_mask + 1; |
729 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
730 | } |
731 | |
f9ea940f |
732 | // Korea mapping, 1 selectable 16KB bank at the top |
733 | static void write_bank_korea(unsigned short a, unsigned char d) |
734 | { |
735 | if (a != 0xa000) return; |
736 | if (Pico.ms.mapper != PMS_MAP_KOREA && (Pico.ms.mapper)) return; |
737 | elprintf(EL_Z80BNK, "bank korea %04x %02x @ %04x", a, d, z80_pc()); |
738 | Pico.ms.mapper = PMS_MAP_KOREA; |
739 | Pico.ms.carthw[0xf] = d; |
740 | |
741 | d &= bank_mask; |
742 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); |
743 | } |
744 | |
745 | // Korean n-in-1 mapping. 1 selectable 32KB bank at the bottom |
1cdc3f84 |
746 | static void write_bank_n32k(unsigned short a, unsigned char d) |
1bb3f2cc |
747 | { |
f9ea940f |
748 | if (a != 0xffff) return; |
1cdc3f84 |
749 | // code must be in RAM since all visible ROM space is swapped |
750 | if (Pico.ms.mapper != PMS_MAP_N32K && (Pico.ms.mapper || z80_pc() < 0xc000)) return; |
1bb3f2cc |
751 | elprintf(EL_Z80BNK, "bank 32k %04x %02x @ %04x", a, d, z80_pc()); |
1cdc3f84 |
752 | Pico.ms.mapper = PMS_MAP_N32K; |
1bb3f2cc |
753 | Pico.ms.carthw[0xf] = d; |
754 | |
755 | d &= bank_mask >> 1; |
1cdc3f84 |
756 | z80_map_set(z80_read_map, 0, 0x7fff, Pico.rom + (d << 15), 0); |
757 | } |
758 | |
f9ea940f |
759 | // Korean 4-in-1. 2 selectable 16KB banks, top bank is shifted by bottom one |
1cdc3f84 |
760 | static void write_bank_n16k(unsigned short a, unsigned char d) |
761 | { |
f9ea940f |
762 | if (a != 0x3ffe && a != 0x7fff && a != 0xbfff) return; |
1cdc3f84 |
763 | // code must be in RAM since all visible ROM space is swapped |
764 | if (Pico.ms.mapper != PMS_MAP_N16K && (Pico.ms.mapper || z80_pc() < 0xc000)) return; |
765 | elprintf(EL_Z80BNK, "bank 16k %04x %02x @ %04x", a, d, z80_pc()); |
766 | Pico.ms.mapper = PMS_MAP_N16K; |
767 | Pico.ms.carthw[a>>14] = d; |
768 | |
769 | d &= bank_mask; |
770 | a = a & 0xc000; |
771 | // the top bank shifts with the bottom bank. |
772 | if (a == 0x8000) d += Pico.ms.carthw[0] & 0x30; |
773 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
1bb3f2cc |
774 | } |
775 | |
f9ea940f |
776 | // MSX-Nemesis mapper. 4 selectable 8KB banks at the top |
777 | static void write_bank_msxn(unsigned short a, unsigned char d) |
778 | { |
779 | if (a > 0x0003) return; |
780 | // never autodetected, selectable only via config |
781 | if (Pico.ms.mapper != PMS_MAP_NEMESIS) return; |
782 | elprintf(EL_Z80BNK, "bank nems %04x %02x @ %04x", a, d, z80_pc()); |
783 | Pico.ms.carthw[a] = d; |
784 | |
785 | a = (a^2)*0x2000 + 0x4000; |
786 | d &= 2*bank_mask + 1; |
787 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
788 | } |
789 | |
790 | // Korean Janggun mapper. 4 selectable 8KB banks at the top, hardware byte flip |
791 | static unsigned char read_flipped_jang(unsigned a) |
792 | { |
793 | static unsigned char flipper[16] = // reversed nibble bit order |
794 | { 0x0,0x8,0x4,0xc,0x2,0xa,0x6,0xe,0x1,0x9,0x5,0xd,0x3,0xb,0x7,0xf }; |
795 | unsigned char c; |
796 | |
797 | // return value at address a in reversed bit order |
798 | c = Pico.rom[(Pico.ms.carthw[a>>13] << 13) + (a & 0x1fff)]; |
799 | return (flipper[c&0xf]<<4) | flipper[c>>4]; |
800 | } |
801 | |
802 | static void write_bank_jang(unsigned short a, unsigned char d) |
803 | { |
804 | // address is 0xfffe, 0xffff, 0x4000, 0x6000, 0x8000, 0xa000 |
b7209504 |
805 | if ((a|1) != 0xffff && (a < 0x4000 || a > 0xa000 || (a & 0x1fff))) return; |
f9ea940f |
806 | // never autodetected, selectable only via config |
807 | if (Pico.ms.mapper != PMS_MAP_JANGGUN) return; |
808 | elprintf(EL_Z80BNK, "bank jang %04x %02x @ %04x", a, d, z80_pc()); |
809 | |
810 | if ((a|1) == 0xffff) { |
811 | int x = a & 1, f = d & 0x40; |
812 | Pico.ms.carthw[x] = d; |
813 | d &= bank_mask; |
814 | Pico.ms.carthw[2*x + 2] = 2*d, Pico.ms.carthw[2*x + 3] = 2*d+1; |
815 | a = (x+1) * 0x4000; |
816 | if (!f) |
817 | z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0); |
818 | else |
819 | z80_map_set(z80_read_map, a, a+0x3fff, read_flipped_jang, 1); |
820 | } else { |
821 | d &= 2*bank_mask + 1; |
822 | Pico.ms.carthw[a>>13] = d; |
823 | if (!(Pico.ms.carthw[(a>>15)&1] & 0x40)) |
824 | z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); |
825 | else |
826 | z80_map_set(z80_read_map, a, a+0x1fff, read_flipped_jang, 1); |
827 | } |
828 | } |
829 | |
7c6f7914 |
830 | // Korean 188-in-1. 4 8KB banks from 0x4000, selected by xor'd bank index |
831 | static void write_bank_xor(unsigned short a, unsigned char d) |
832 | { |
833 | // 4x8KB bank select @0x2000 |
b7209504 |
834 | if ((a&0xff00) != 0x2000) return; |
7c6f7914 |
835 | if (Pico.ms.mapper != PMS_MAP_XOR && Pico.ms.mapper) return; |
836 | |
96948bdf |
837 | elprintf(EL_Z80BNK, "bank xor %04x %02x @ %04x", a, d, z80_pc()); |
7c6f7914 |
838 | Pico.ms.mapper = PMS_MAP_XOR; |
839 | |
840 | Pico.ms.carthw[0] = d; |
841 | z80_map_set(z80_read_map, 0x4000, 0x5fff, Pico.rom + ((d^0x1f) << 13), 0); |
842 | z80_map_set(z80_read_map, 0x6000, 0x7fff, Pico.rom + ((d^0x1e) << 13), 0); |
843 | z80_map_set(z80_read_map, 0x8000, 0x9fff, Pico.rom + ((d^0x1d) << 13), 0); |
844 | z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + ((d^0x1c) << 13), 0); |
845 | } |
846 | |
3611781e |
847 | // SG-1000 8KB RAM Adaptor mapper. 8KB RAM at address 0x2000 |
aaef4b94 |
848 | static void write_bank_x8k(unsigned short a, unsigned char d) |
849 | { |
df6c895c |
850 | // 8KB address range @ 0x2000 (adaptor) or @ 0x8000 (cartridge) |
b7209504 |
851 | if (((a&0xe000) != 0x2000 && (a&0xe000) != 0x8000) || (a & 0x0f) == 5) return; |
df6c895c |
852 | if (Pico.ms.mapper != PMS_MAP_8KBRAM && Pico.ms.mapper) return; |
853 | |
171fb8cc |
854 | elprintf(EL_Z80BNK, "bank x8k %04x %02x @ %04x", a, d, z80_pc()); |
7c16d135 |
855 | ((unsigned char *)(PicoMem.vram+0x4000))[a&0x1fff] = d; |
3611781e |
856 | Pico.ms.mapper = PMS_MAP_8KBRAM; |
171fb8cc |
857 | |
216c9f17 |
858 | a &= 0xe000; |
df6c895c |
859 | Pico.ms.carthw[0] = a >> 12; |
216c9f17 |
860 | z80_map_set(z80_read_map, a, a+0x1fff, PicoMem.vram+0x4000, 0); |
861 | z80_map_set(z80_write_map, a, a+0x1fff, PicoMem.vram+0x4000, 0); |
aaef4b94 |
862 | } |
863 | |
cab84f29 |
864 | // SC-3000 32KB RAM mapper for BASIC level IIIB. 32KB RAM at address 0x8000 |
865 | static void write_bank_x32k(unsigned short a, unsigned char d) |
866 | { |
867 | // 32KB address range @ 0x8000 |
868 | if ((a&0xc000) != 0x8000) return; |
869 | if (Pico.ms.mapper != PMS_MAP_32KBRAM && |
870 | (Pico.ms.mapper || Pico.romsize > 0x8000)) return; |
871 | |
872 | elprintf(EL_Z80BNK, "bank x32k %04x %02x @ %04x", a, d, z80_pc()); |
873 | ((unsigned char *)(PicoMem.vram+0x4000))[a&0x7fff] = d; |
874 | Pico.ms.mapper = PMS_MAP_32KBRAM; |
875 | |
876 | a &= 0xc000; |
877 | Pico.ms.carthw[0] = a >> 12; |
878 | // NB this deactivates internal RAM and all mapper detection |
92064e2f |
879 | memcpy(PicoMem.vram+0x4000+(0x8000-0x2000)/2, PicoMem.zram, 0x2000); |
cab84f29 |
880 | z80_map_set(z80_read_map, a, a+0x7fff, PicoMem.vram+0x4000, 0); |
881 | z80_map_set(z80_write_map, a, a+0x7fff, PicoMem.vram+0x4000, 0); |
882 | } |
883 | |
df6c895c |
884 | char *mappers[] = { |
885 | [PMS_MAP_SEGA] = "Sega", |
886 | [PMS_MAP_CODEM] = "Codemasters", |
887 | [PMS_MAP_KOREA] = "Korea", |
888 | [PMS_MAP_MSX] = "Korea MSX", |
889 | [PMS_MAP_N32K] = "Korea X-in-1", |
890 | [PMS_MAP_N16K] = "Korea 4-Pak", |
891 | [PMS_MAP_JANGGUN] = "Korea Janggun", |
892 | [PMS_MAP_NEMESIS] = "Korea Nemesis", |
893 | [PMS_MAP_8KBRAM] = "Taiwan 8K RAM", |
7c6f7914 |
894 | [PMS_MAP_XOR] = "Korea XOR", |
cab84f29 |
895 | [PMS_MAP_32KBRAM] = "Sega 32K RAM", |
df6c895c |
896 | }; |
897 | |
f9ea940f |
898 | // TODO auto-selecting is not really reliable. |
1cdc3f84 |
899 | // Before adding more mappers this should be revised. |
553c3eaa |
900 | static void xwrite(unsigned int a, unsigned char d) |
2ec9bec5 |
901 | { |
b7209504 |
902 | int sz = (/*PicoIn.AHW & (PAHW_SG|PAHW_SC) ? 2 :*/ 8) * 1024; |
0aa63fce |
903 | |
200772b7 |
904 | elprintf(EL_IO, "z80 write [%04x] %02x", a, d); |
2ec9bec5 |
905 | if (a >= 0xc000) |
0aa63fce |
906 | PicoMem.zram[a & (sz-1)] = d; |
032c76a3 |
907 | |
f9ea940f |
908 | switch (Pico.ms.mapper) { // via config, or auto detected |
909 | case PMS_MAP_SEGA: write_bank_sega(a, d); break; |
910 | case PMS_MAP_CODEM: write_bank_codem(a, d); break; |
911 | case PMS_MAP_MSX: write_bank_msx(a, d); break; |
912 | case PMS_MAP_KOREA: write_bank_korea(a, d); break; |
913 | case PMS_MAP_N32K: write_bank_n32k(a, d); break; |
914 | case PMS_MAP_N16K: write_bank_n16k(a, d); break; |
915 | case PMS_MAP_JANGGUN: write_bank_jang(a, d); break; |
916 | case PMS_MAP_NEMESIS: write_bank_msxn(a, d); break; |
171fb8cc |
917 | case PMS_MAP_8KBRAM: write_bank_x8k(a, d); break; |
cab84f29 |
918 | case PMS_MAP_32KBRAM: write_bank_x32k(a, d); break; |
7c6f7914 |
919 | case PMS_MAP_XOR: write_bank_xor(a, d); break; |
f9ea940f |
920 | |
921 | case PMS_MAP_AUTO: |
df6c895c |
922 | // disable autodetection after some time |
b7209504 |
923 | if ((a >= 0xc000 && a < 0xfff8) || Pico.ms.mapcnt > 50) break; |
f9ea940f |
924 | // NB the sequence of mappers is crucial for the auto detection |
0aa63fce |
925 | if (PicoIn.AHW & PAHW_SC) { |
cab84f29 |
926 | write_bank_x32k(a,d); |
0aa63fce |
927 | } else if (PicoIn.AHW & PAHW_SG) { |
df6c895c |
928 | write_bank_x8k(a, d); |
cab84f29 |
929 | } else { |
df6c895c |
930 | write_bank_n32k(a, d); |
931 | write_bank_sega(a, d); |
932 | write_bank_msx(a, d); |
933 | write_bank_codem(a, d); |
934 | write_bank_korea(a, d); |
935 | write_bank_n16k(a, d); |
7c6f7914 |
936 | write_bank_xor(a, d); |
e48f3f27 |
937 | } |
df6c895c |
938 | |
939 | Pico.ms.mapcnt ++; |
940 | if (Pico.ms.mapper) |
941 | elprintf(EL_STATUS, "autodetected %s mapper",mappers[Pico.ms.mapper]); |
f9ea940f |
942 | break; |
943 | } |
3e49ffd0 |
944 | } |
945 | |
c9076db9 |
946 | // Try to detect some tricky cases by their TMR header |
947 | // NB Codemasters, some Betas, most unlicensed games have no or invalid TMRs. |
948 | // if the cksum header is valid mark this by 0x.fff.... and use that instead |
949 | |
96948bdf |
950 | // TMR product codes and hardware type for known 50Hz-only games |
c9076db9 |
951 | static u32 region_pal[] = { // cf Meka, meka/meka.nam |
afdc2ed4 |
952 | 0x40207067 /* Addams Family */, 0x40207020 /* Back.Future 3 */, |
953 | 0x40207058 /* Battlemaniacs */, 0x40007105 /* Cal.Games 2 */, |
67c9ba38 |
954 | 0x402f7065 /* Dracula */ , 0x40007109 /* Home Alone */, |
afdc2ed4 |
955 | 0x40009024 /* Pwr.Strike 2 */ , 0x40207047 /* Predator 2 EU */, |
956 | 0x40002519 /* Quest.Yak */ , 0x40207064 /* Robocop 3 */, |
67c9ba38 |
957 | 0x4f205014 /* Sens.Soccer */ , 0x40002573 /* Sonic Blast */, |
afdc2ed4 |
958 | 0x40007080 /* S.Harrier EU */ , 0x40007038 /* Taito Chase */, |
67c9ba38 |
959 | 0x40009015 /* Sonic 2 EU */ , /* NBA Jam: no valid id/cksum */ |
c9076db9 |
960 | 0x4fff8872 /* Excell.Dizzy */ , 0x4ffffac4 /* Fantast.Dizzy */, |
961 | 0x4fff4a89 /* Csm.Spacehead */, 0x4fffe352 /* Micr.Machines */, |
67c9ba38 |
962 | 0x4fffa203 /* Bad Apple */ |
c9076db9 |
963 | }; |
964 | |
965 | // TMR product codes and hardware type for known non-FM games |
966 | static u32 no_fmsound[] = { // cf Meka, meka/meka.pat |
67c9ba38 |
967 | 0x40002070 /* Walter Payton */, 0x40017020 /* American Pro */, |
c9076db9 |
968 | 0x4fffe890 /* Wanted */ |
afdc2ed4 |
969 | }; |
970 | |
c9076db9 |
971 | // TMR product codes and hardware type for known GG carts running in SMS mode |
972 | // NB GG carts having the system type set to 4 (eg. HTH games) run as SMS anyway |
973 | static u32 gg_smsmode[] = { // cf https://www.smspower.org/Tags/SMS-GG |
67c9ba38 |
974 | 0x60002401 /* Castl.Ilusion */, 0x6f101018 /* Taito Chase */, |
40172b93 |
975 | 0x70709018 /* Olympic Gold */ , 0x70709038 /* Outrun EU */, |
ca206ba1 |
976 | 0x60801068 /* Predator 2 */ , 0x70408098 /* Prince.Persia */, |
67c9ba38 |
977 | 0x50101037 /* Rastan Saga */ , 0x7f086018 /* RC Grandprix */, |
ca206ba1 |
978 | 0x60002415 /* Super Kickoff */, 0x60801108 /* WWF.Steelcage */, |
c9076db9 |
979 | /* Excell.Dizzy, Fantast.Dizzy, Super Tetris: no valid id/cksum in TMR */ |
67c9ba38 |
980 | 0x4f813028 /* Tesserae */ |
ca206ba1 |
981 | }; |
982 | |
721f2651 |
983 | // TMR product codes and hardware type for known games using 3-D glasses |
984 | static u32 three_dee[] = { |
985 | 0x4f008001 /* Missile Def. */ , 0x40008007 /* Out Run 3-D */, |
986 | 0x40008006 /* Poseidon Wars */, 0x40008004 /* Space Harrier */, |
987 | 0x40008002 /* Zaxxon 3-D */ , 0x4fff8793 /* Maze Hunter */ |
988 | }; |
989 | |
2ec9bec5 |
990 | void PicoResetMS(void) |
3e49ffd0 |
991 | { |
f9ea940f |
992 | unsigned tmr; |
c9076db9 |
993 | u32 id, hw, ck, i; |
032c76a3 |
994 | |
f9ea940f |
995 | // set preselected hw/mapper from config |
280bfc3c |
996 | if (PicoIn.hwSelect) { |
0aa63fce |
997 | PicoIn.AHW &= ~(PAHW_GG|PAHW_SG|PAHW_SC); |
280bfc3c |
998 | switch (PicoIn.hwSelect) { |
0aa63fce |
999 | case PHWS_GG: PicoIn.AHW |= PAHW_GG; break; |
1000 | case PHWS_SG: PicoIn.AHW |= PAHW_SG; break; |
1001 | case PHWS_SC: PicoIn.AHW |= PAHW_SC; break; |
280bfc3c |
1002 | } |
f9ea940f |
1003 | } |
df6c895c |
1004 | Pico.ms.mapcnt = Pico.ms.mapper = 0; |
f9ea940f |
1005 | if (PicoIn.mapper) |
1006 | Pico.ms.mapper = PicoIn.mapper; |
3611781e |
1007 | Pico.m.hardware |= PMS_HW_JAP; // default region Japan if no TMR header |
e48f3f27 |
1008 | if (PicoIn.regionOverride > 2) |
1009 | Pico.m.hardware &= ~PMS_HW_JAP; |
c9076db9 |
1010 | Pico.m.hardware |= PMS_HW_FM; |
1011 | if (!(PicoIn.opt & POPT_EN_YM2413)) |
1012 | Pico.m.hardware &= ~PMS_HW_FM; |
f9ea940f |
1013 | |
1014 | // check if the ROM header contains more system information |
1015 | for (tmr = 0x2000; tmr < 0xbfff && tmr <= Pico.romsize; tmr *= 2) { |
1016 | if (!memcmp(Pico.rom + tmr-16, "TMR SEGA", 8)) { |
1017 | hw = Pico.rom[tmr-1] >> 4; |
67c9ba38 |
1018 | id = CPU_LE4(*(u32 *)&Pico.rom[tmr-4]); |
721f2651 |
1019 | ck = (CPU_LE4(*(u32 *)&Pico.rom[tmr-8])>>16) | (id&0xf0000000) | 0xfff0000; |
67c9ba38 |
1020 | |
40172b93 |
1021 | if (!PicoIn.hwSelect && !PicoIn.AHW && hw && ((id+1)&0xfffe) != 0) { |
55615f9c |
1022 | if (hw >= 0x5 && hw < 0x8) |
0aa63fce |
1023 | PicoIn.AHW |= PAHW_GG; // GG cartridge detected |
55615f9c |
1024 | } |
1025 | if (!PicoIn.regionOverride) { |
3611781e |
1026 | Pico.m.hardware &= ~PMS_HW_JAP; |
55615f9c |
1027 | if (hw == 0x5 || hw == 0x3) |
3611781e |
1028 | Pico.m.hardware |= PMS_HW_JAP; // region Japan |
55615f9c |
1029 | } |
afdc2ed4 |
1030 | for (i = 0; i < sizeof(region_pal)/sizeof(*region_pal); i++) |
c9076db9 |
1031 | if ((id == region_pal[i] || ck == region_pal[i]) && !PicoIn.regionOverride) |
1032 | { |
afdc2ed4 |
1033 | Pico.m.pal = 1; // requires 50Hz timing |
1034 | break; |
1035 | } |
ca206ba1 |
1036 | for (i = 0; i < sizeof(gg_smsmode)/sizeof(*gg_smsmode); i++) |
c9076db9 |
1037 | if ((id == gg_smsmode[i] || ck == gg_smsmode[i]) && !PicoIn.hwSelect) { |
ca206ba1 |
1038 | PicoIn.AHW &= ~PAHW_GG; // requires SMS mode |
67c9ba38 |
1039 | if (hw < 0x5) PicoIn.AHW |= PAHW_GG; |
ca206ba1 |
1040 | break; |
1041 | } |
c9076db9 |
1042 | for (i = 0; i < sizeof(no_fmsound)/sizeof(*no_fmsound); i++) |
1043 | if ((id == no_fmsound[i] || ck == no_fmsound[i])) { |
1044 | Pico.m.hardware &= ~PMS_HW_FM; // incompatible with FM |
1045 | break; |
1046 | } |
721f2651 |
1047 | for (i = 0; i < sizeof(three_dee)/sizeof(*three_dee); i++) |
1048 | if ((id == three_dee[i] || ck == three_dee[i])) { |
1049 | Pico.m.hardware |= PMS_HW_3D; // uses 3-D glasses |
1050 | break; |
1051 | } |
f9ea940f |
1052 | break; |
280bfc3c |
1053 | } |
1054 | } |
1055 | |
f9ea940f |
1056 | z80_reset(); |
1057 | PsndReset(); // pal must be known here |
92064e2f |
1058 | PicoCloseTape(); |
4af69a1d |
1059 | |
1060 | Pico.ms.io_ctl = (PicoIn.AHW & (PAHW_SG|PAHW_SC)) ? 0xf5 : 0xff; |
f9ea940f |
1061 | Pico.ms.fm_ctl = 0xff; |
f9ea940f |
1062 | |
032c76a3 |
1063 | // reset memory mapping |
1064 | PicoMemSetupMS(); |
1c70532f |
1065 | |
1bb3f2cc |
1066 | // BIOS, VDP intialisation |
1c70532f |
1067 | Pico.video.reg[0] = 0x36; |
1068 | Pico.video.reg[1] = 0xa0; |
1069 | Pico.video.reg[2] = 0xff; |
1070 | Pico.video.reg[3] = 0xff; |
1071 | Pico.video.reg[4] = 0xff; |
1072 | Pico.video.reg[5] = 0xff; |
1073 | Pico.video.reg[6] = 0xfb; |
1074 | Pico.video.reg[7] = 0x00; |
1075 | Pico.video.reg[8] = 0x00; |
1076 | Pico.video.reg[9] = 0x00; |
1077 | Pico.video.reg[10] = 0xff; |
4af69a1d |
1078 | Pico.m.dirtyPal = 1; |
1bb3f2cc |
1079 | |
f9ea940f |
1080 | // BIOS, clear zram (unitialized on Mark-III, cf src/mame/drivers/sms.cpp) |
0aa63fce |
1081 | i = !(PicoIn.AHW & PAHW_GG) && (Pico.m.hardware & PMS_HW_JAP) ? 0xf0 : 0x00; |
3611781e |
1082 | memset(PicoMem.zram, i, sizeof(PicoMem.zram)); |
3e49ffd0 |
1083 | } |
1084 | |
1085 | void PicoPowerMS(void) |
1086 | { |
87b0845f |
1087 | int s, tmp; |
1088 | |
88fd63ad |
1089 | memset(&PicoMem,0,sizeof(PicoMem)); |
2ec9bec5 |
1090 | memset(&Pico.video,0,sizeof(Pico.video)); |
1091 | memset(&Pico.m,0,sizeof(Pico.m)); |
2ec9bec5 |
1092 | |
87b0845f |
1093 | // calculate a mask for bank writes. |
1094 | // ROM loader has aligned the size for us, so this is safe. |
1095 | s = 0; tmp = Pico.romsize; |
1096 | while ((tmp >>= 1) != 0) |
1097 | s++; |
1098 | if (Pico.romsize > (1 << s)) |
1099 | s++; |
1100 | tmp = 1 << s; |
1101 | bank_mask = (tmp - 1) >> 14; |
1102 | |
1cdc3f84 |
1103 | PicoMem.ioports[0] = 0xc3; // hack to jump @0 at end of RAM to wrap around |
f9ea940f |
1104 | Pico.ms.mapper = PicoIn.mapper; |
2ec9bec5 |
1105 | PicoReset(); |
3e49ffd0 |
1106 | } |
1107 | |
1108 | void PicoMemSetupMS(void) |
1109 | { |
7c16d135 |
1110 | u8 mapper = Pico.ms.mapper; |
b7209504 |
1111 | int sz = (/*PicoIn.AHW & (PAHW_SG|PAHW_SC) ? 2 :*/ 8) * 1024; |
0aa63fce |
1112 | u32 a; |
7c16d135 |
1113 | |
0aa63fce |
1114 | // RAM and its mirrors |
1115 | for (a = 0xc000; a < 0x10000; a += sz) { |
1116 | z80_map_set(z80_read_map, a, a + sz-1, PicoMem.zram, 0); |
1117 | z80_map_set(z80_write_map, a, a + sz-1, PicoMem.zram, 0); |
1118 | } |
b7209504 |
1119 | a = 0xffff - (1<<Z80_MEM_SHIFT); |
1120 | z80_map_set(z80_write_map, a+1, 0xffff, xwrite, 1); // mapper detection |
3e49ffd0 |
1121 | |
0aa63fce |
1122 | // ROM |
1123 | z80_map_set(z80_read_map, 0x0000, 0xbfff, Pico.rom, 0); |
1124 | z80_map_set(z80_write_map, 0x0000, 0xbfff, xwrite, 1); // mapper detection |
1ce2b092 |
1125 | |
92064e2f |
1126 | // SC-3000 has 2KB, but no harm in mapping the 32KB for BASIC here |
1127 | if ((PicoIn.AHW & PAHW_SC) && mapper == PMS_MAP_AUTO) |
1128 | mapper = PMS_MAP_32KBRAM; |
f9ea940f |
1129 | // Nemesis mapper maps last 8KB rom bank #15 to adress 0 |
7c16d135 |
1130 | if (mapper == PMS_MAP_NEMESIS && Pico.romsize > 0x1e000) |
f9ea940f |
1131 | z80_map_set(z80_read_map, 0x0000, 0x1fff, Pico.rom + 0x1e000, 0); |
92064e2f |
1132 | |
3e49ffd0 |
1133 | #ifdef _USE_DRZ80 |
1134 | drZ80.z80_in = z80_sms_in; |
1135 | drZ80.z80_out = z80_sms_out; |
1136 | #endif |
1137 | #ifdef _USE_CZ80 |
3e49ffd0 |
1138 | Cz80_Set_INPort(&CZ80, z80_sms_in); |
1139 | Cz80_Set_OUTPort(&CZ80, z80_sms_out); |
1140 | #endif |
032c76a3 |
1141 | |
1cdc3f84 |
1142 | // memory mapper setup, linear mapping of 1st 48KB |
7c16d135 |
1143 | memset(Pico.ms.carthw, 0, sizeof(Pico.ms.carthw)); |
f9ea940f |
1144 | if (mapper == PMS_MAP_MSX || mapper == PMS_MAP_NEMESIS) { |
1cdc3f84 |
1145 | xwrite(0x0000, 4); |
1146 | xwrite(0x0001, 5); |
1147 | xwrite(0x0002, 2); |
1148 | xwrite(0x0003, 3); |
7c16d135 |
1149 | } else if (mapper == PMS_MAP_KOREA) { |
1150 | xwrite(0xa000, 2); |
f9ea940f |
1151 | } else if (mapper == PMS_MAP_N32K) { |
1bb3f2cc |
1152 | xwrite(0xffff, 0); |
f9ea940f |
1153 | } else if (mapper == PMS_MAP_N16K) { |
1154 | xwrite(0x3ffe, 0); |
1155 | xwrite(0x7fff, 1); |
1156 | xwrite(0xbfff, 2); |
1157 | } else if (mapper == PMS_MAP_JANGGUN) { |
1158 | xwrite(0xfffe, 1); |
1159 | xwrite(0xffff, 2); |
7c6f7914 |
1160 | } else if (mapper == PMS_MAP_XOR) { |
1161 | xwrite(0x2000, 0); |
f9ea940f |
1162 | } else if (mapper == PMS_MAP_CODEM) { |
1cdc3f84 |
1163 | xwrite(0x0000, 0); |
1164 | xwrite(0x4000, 1); |
1165 | xwrite(0x8000, 2); |
7c6f7914 |
1166 | } else if (mapper == PMS_MAP_SEGA) { |
8ba60e31 |
1167 | xwrite(0xfffc, 0); |
1168 | xwrite(0xfffd, 0); |
1169 | xwrite(0xfffe, 1); |
1170 | xwrite(0xffff, 2); |
92064e2f |
1171 | } else if (mapper == PMS_MAP_32KBRAM) { |
1172 | xwrite(0x8000, 0); |
7c6f7914 |
1173 | } else if (mapper == PMS_MAP_AUTO) { |
1174 | // pre-initialize Sega mapper to linear mapping (else state load may fail) |
1175 | Pico.ms.carthw[0xe] = 0x1; |
1176 | Pico.ms.carthw[0xf] = 0x2; |
8ba60e31 |
1177 | } |
3e49ffd0 |
1178 | } |
1179 | |
b4db550e |
1180 | void PicoStateLoadedMS(void) |
1181 | { |
8ba60e31 |
1182 | u8 mapper = Pico.ms.mapper; |
da414888 |
1183 | u8 zram_dff0[16]; // TODO xwrite also writes to zram :-/ |
638a8350 |
1184 | u8 carthw[16]; |
da414888 |
1185 | |
1186 | memcpy(zram_dff0, PicoMem.zram+0x1ff0, 16); |
638a8350 |
1187 | memcpy(carthw, Pico.ms.carthw, 16); |
1188 | memset(Pico.ms.carthw, -1, 16); |
cab84f29 |
1189 | if (mapper == PMS_MAP_8KBRAM || mapper == PMS_MAP_32KBRAM) { |
638a8350 |
1190 | u16 a = carthw[0] << 12; |
7c16d135 |
1191 | xwrite(a, *(unsigned char *)(PicoMem.vram+0x4000)); |
1192 | } else if (mapper == PMS_MAP_MSX || mapper == PMS_MAP_NEMESIS) { |
638a8350 |
1193 | xwrite(0x0000, carthw[0]); |
1194 | xwrite(0x0001, carthw[1]); |
1195 | xwrite(0x0002, carthw[2]); |
1196 | xwrite(0x0003, carthw[3]); |
7c16d135 |
1197 | } else if (mapper == PMS_MAP_KOREA) { |
638a8350 |
1198 | xwrite(0xa000, carthw[0x0f]); |
7c16d135 |
1199 | } else if (mapper == PMS_MAP_N32K) { |
638a8350 |
1200 | xwrite(0xffff, carthw[0x0f]); |
7c16d135 |
1201 | } else if (mapper == PMS_MAP_N16K) { |
638a8350 |
1202 | xwrite(0x3ffe, carthw[0]); |
1203 | xwrite(0x7fff, carthw[1]); |
1204 | xwrite(0xbfff, carthw[2]); |
7c16d135 |
1205 | } else if (mapper == PMS_MAP_JANGGUN) { |
638a8350 |
1206 | xwrite(0x4000, carthw[2]); |
1207 | xwrite(0x6000, carthw[3]); |
1208 | xwrite(0x8000, carthw[4]); |
1209 | xwrite(0xa000, carthw[5]); |
7c6f7914 |
1210 | } else if (mapper == PMS_MAP_XOR) { |
638a8350 |
1211 | xwrite(0x2000, carthw[0]); |
7c16d135 |
1212 | } else if (mapper == PMS_MAP_CODEM) { |
638a8350 |
1213 | xwrite(0x0000, carthw[0]); |
1214 | xwrite(0x4000, carthw[1]); |
1215 | xwrite(0x8000, carthw[2]); |
7c16d135 |
1216 | } else if (mapper == PMS_MAP_SEGA) { |
638a8350 |
1217 | xwrite(0xfffc, carthw[0x0c]); |
1218 | xwrite(0xfffd, carthw[0x0d]); |
1219 | xwrite(0xfffe, carthw[0x0e]); |
1220 | xwrite(0xffff, carthw[0x0f]); |
032c76a3 |
1221 | } |
da414888 |
1222 | memcpy(PicoMem.zram+0x1ff0, zram_dff0, 16); |
638a8350 |
1223 | memcpy(Pico.ms.carthw, carthw, 16); |
b4db550e |
1224 | } |
1225 | |
3e49ffd0 |
1226 | void PicoFrameMS(void) |
1227 | { |
2ec9bec5 |
1228 | struct PicoVideo *pv = &Pico.video; |
1229 | int is_pal = Pico.m.pal; |
1230 | int lines = is_pal ? 313 : 262; |
c644ce99 |
1231 | int cycles_line = 228; |
93f9619e |
1232 | int skip = PicoIn.skipFrame; |
2ec9bec5 |
1233 | int lines_vis = 192; |
87b0845f |
1234 | int hint; // Hint counter |
835122bc |
1235 | int nmi; |
2ec9bec5 |
1236 | int y; |
1237 | |
4f2cdbf5 |
1238 | PsndStartFrame(); |
1239 | |
d5908845 |
1240 | // for SMS the pause button generates an NMI, for GG ths is not the case |
93f9619e |
1241 | nmi = (PicoIn.pad[0] >> 7) & 1; |
92064e2f |
1242 | if ((PicoIn.AHW & PAHW_8BIT) == PAHW_SMS && !Pico.ms.nmi_state && nmi) |
835122bc |
1243 | z80_nmi(); |
1244 | Pico.ms.nmi_state = nmi; |
1245 | |
4b3e9d92 |
1246 | if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) |
1247 | lines_vis = (pv->reg[1] & 0x08) ? 240 : 224; |
0df7401c |
1248 | PicoFrameStartSMS(); |
87b0845f |
1249 | hint = pv->reg[0x0a]; |
200772b7 |
1250 | |
1ce2b092 |
1251 | // SMS: xscroll:f3 sprovr,vint, vcount:fc, hint:fd |
1252 | // GG: xscroll:f5 sprovr,vint:fd vcount:fe, hint:ff |
2ec9bec5 |
1253 | for (y = 0; y < lines; y++) |
1254 | { |
1ce2b092 |
1255 | Pico.t.z80c_line_start = Pico.t.z80c_aim; |
1256 | |
1257 | // advance the line counter. It is set back at some point in the VBLANK so |
1258 | // that the line count in the active area (-32..lines+1) is contiguous. |
1259 | pv->v_counter = Pico.m.scanline = (u8)y; |
7eeb85be |
1260 | switch (is_pal ? -lines_vis : lines_vis) { |
1261 | case 192: if (y > 218) pv->v_counter = y - (lines-256); break; |
1262 | case 224: if (y > 234) pv->v_counter = y - (lines-256); break; |
67c9ba38 |
1263 | /* case 240: if (y > 242) pv->v_counter = y - (lines-256); break; ? */ |
7eeb85be |
1264 | case -192: if (y > 242) pv->v_counter = y - (lines-256); break; |
1265 | case -224: if (y > 258) pv->v_counter = y - (lines-256); break; |
1266 | case -240: if (y > 266) pv->v_counter = y - (lines-256); break; |
1267 | } |
2ec9bec5 |
1268 | |
1ce2b092 |
1269 | // Parse sprites for the next line |
1270 | if (y < lines_vis) |
1271 | PicoParseSATSMS(y-1); |
1272 | else if (y > lines-32) |
1273 | PicoParseSATSMS(y-1-lines); |
1274 | |
1ce2b092 |
1275 | // take over status bits from previously rendered line TODO: cycle exact? |
1276 | pv->status |= sprites_status; |
1277 | sprites_status = 0; |
7eeb85be |
1278 | |
985bbf1c |
1279 | // Interrupt handling. Simulate interrupt flagged and immediately reset in |
1280 | // same insn by flagging the irq, execute for 1 insn, then checking if the |
1281 | // irq is still pending. (GG Chicago, SMS Back to the Future III) |
1ce2b092 |
1282 | pv->pending_ints &= ~2; // lost if not caught in the same line |
87b0845f |
1283 | if (y <= lines_vis) |
1284 | { |
1285 | if (--hint < 0) |
1286 | { |
1287 | hint = pv->reg[0x0a]; |
1288 | pv->pending_ints |= 2; |
7eeb85be |
1289 | z80_exec(Pico.t.z80c_cnt + 1); |
1290 | |
985bbf1c |
1291 | if ((pv->reg[0] & 0x10) && (pv->pending_ints & 2)) { |
87b0845f |
1292 | elprintf(EL_INTS, "hint"); |
cf83610b |
1293 | z80_int_assert(1); |
1bb3f2cc |
1294 | } |
87b0845f |
1295 | } |
1296 | } |
200772b7 |
1297 | else if (y == lines_vis + 1) { |
87b0845f |
1298 | pv->pending_ints |= 1; |
7eeb85be |
1299 | z80_exec(Pico.t.z80c_cnt + 1); |
985bbf1c |
1300 | |
1301 | if ((pv->reg[1] & 0x20) && (pv->pending_ints & 1)) { |
2ec9bec5 |
1302 | elprintf(EL_INTS, "vint"); |
cf83610b |
1303 | z80_int_assert(1); |
985bbf1c |
1304 | } |
2ec9bec5 |
1305 | } |
2db0d004 |
1306 | z80_exec(Pico.t.z80c_line_start + 12); // GG Madou 1, display off after line start |
1307 | |
1308 | // render next line |
1309 | if (y < lines_vis && !skip) |
1310 | PicoLineSMS(y); |
2ec9bec5 |
1311 | |
7eeb85be |
1312 | z80_exec(Pico.t.z80c_line_start + cycles_line); |
2ec9bec5 |
1313 | } |
1314 | |
92064e2f |
1315 | // end of frame updates |
1316 | tape_update(Pico.t.z80c_aim); |
1317 | tape_write(Pico.t.z80c_aim, -1); |
1318 | tape.cycle -= Pico.t.z80c_aim; |
1319 | tape.phase -= Pico.t.z80c_aim; |
1320 | |
274dd51a |
1321 | z80_resetCycles(); |
9f1d5acd |
1322 | PsndGetSamplesMS(lines); |
3e49ffd0 |
1323 | } |
1324 | |
87b0845f |
1325 | void PicoFrameDrawOnlyMS(void) |
1326 | { |
1ce2b092 |
1327 | struct PicoVideo *pv = &Pico.video; |
87b0845f |
1328 | int lines_vis = 192; |
1329 | int y; |
1330 | |
1ce2b092 |
1331 | if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) |
1332 | lines_vis = (pv->reg[1] & 0x08) ? 240 : 224; |
0df7401c |
1333 | PicoFrameStartSMS(); |
87b0845f |
1334 | |
1ce2b092 |
1335 | for (y = 0; y < lines_vis; y++) { |
1336 | PicoParseSATSMS(y-1); |
0df7401c |
1337 | PicoLineSMS(y); |
1ce2b092 |
1338 | } |
87b0845f |
1339 | } |
1340 | |
92064e2f |
1341 | // open tape file for reading (WAV and bitstream files) |
1342 | int PicoPlayTape(const char *fname) |
1343 | { |
1344 | struct tape *pt = &tape; |
1345 | const char *ext = strrchr(fname, '.'); |
1346 | int rate; |
1347 | |
1348 | if (pt->ftape) PicoCloseTape(); |
1349 | pt->ftape = fopen(fname, "rb"); |
1350 | if (pt->ftape == NULL) return 1; |
1351 | pt->mode = 'r'; |
1352 | |
1353 | pt->isbit = ext && ! memcmp(ext, ".bit", 4); |
1354 | if (! pt->isbit) { |
1355 | u8 hdr[44]; |
1356 | int chans; |
1357 | fread(hdr, 1, sizeof(hdr), pt->ftape); |
1358 | // TODO add checks for WAV header... |
1359 | chans = hdr[22] | (hdr[23]<<8); |
1360 | rate = hdr[24] | (hdr[25]<<8) | (hdr[26]<<16) | (hdr[27]<<24); |
1361 | pt->wavsample = 0; |
1362 | pt->fsize = chans*sizeof(s16); |
1363 | } else { |
1364 | rate = 1200; |
1365 | pt->bitsample = ' '; |
1366 | pt->fsize = 1; |
1367 | } |
1368 | |
1369 | pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate; |
1370 | pt->cycles_mult = (1LL<<32) / pt->cycles_sample; |
1371 | pt->cycle = Pico.t.z80c_aim; |
1372 | pt->phase = Pico.t.z80c_aim; |
1373 | pt->pause = 0; |
1374 | return 0; |
1375 | } |
1376 | |
1377 | // open tape file for writing, and write WAV hdr (44KHz, mono, 16 bit samples) |
1378 | int PicoRecordTape(const char *fname) |
1379 | { |
6d885935 |
1380 | const char *ext = strrchr(fname, '.'); |
92064e2f |
1381 | struct tape *pt = &tape; |
6d885935 |
1382 | int rate, i; |
92064e2f |
1383 | |
1384 | if (pt->ftape) PicoCloseTape(); |
1385 | pt->ftape = fopen(fname, "wb"); |
1386 | if (pt->ftape == NULL) return 1; |
1387 | pt->mode = 'w'; |
1388 | |
6d885935 |
1389 | pt->isbit = ext && ! memcmp(ext, ".bit", 4); |
1390 | if (! pt->isbit) { |
1391 | // WAV header "riffraff" for PCM 44KHz mono, 16 bit samples. |
1392 | u8 hdr[44] = { // file and data size updated on file close |
1393 | 'R','I','F','F', 0,0,0,0, 'W','A','V','E', // "RIFF", file size, "WAVE" |
1394 | // "fmt ", hdr size, type, chans, rate, bytes/sec,bytes/sample,bits/sample |
1395 | 'f','m','t',' ', 16,0,0,0, 1,0, 1,0, 68,172,0,0, 136,88,1,0, 2,0, 16,0, |
1396 | 'd','a','t','a', 0,0,0,0 }; // "data", data size |
1397 | |
1398 | rate = 44100; |
1399 | pt->wavsample = 0; // Marker for "don't write yet" |
1400 | pt->fsize = sizeof(s16); |
1401 | |
1402 | fwrite(hdr, 1, sizeof(hdr), pt->ftape); |
1403 | for (i = 0; i < 44100; i++) |
1404 | fwrite(&pt->wavsample, 1, sizeof(s16), pt->ftape); |
1405 | } else { |
1406 | rate = 1200; |
1407 | pt->bitsample = ' '; // Marker for "don't write yet" |
1408 | for (i = 0; i < 1200; i++) |
1409 | fwrite(&pt->bitsample, 1, sizeof(u8), pt->ftape); |
1410 | } |
92064e2f |
1411 | |
6d885935 |
1412 | pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate; |
92064e2f |
1413 | pt->cycles_mult = (1LL<<32) / pt->cycles_sample; |
1414 | pt->cycle = Pico.t.z80c_aim; |
1415 | pt->phase = Pico.t.z80c_aim; |
1416 | pt->pause = 0; |
1417 | return 0; |
1418 | } |
1419 | |
1420 | void PicoCloseTape(void) |
1421 | { |
1422 | struct tape *pt = &tape; |
6d885935 |
1423 | int i, le; |
92064e2f |
1424 | |
1425 | // if recording, write last data, and update length in header |
1426 | if (pt->mode == 'w') { |
6d885935 |
1427 | if (! pt->isbit) { |
1428 | for (i = 0; i < 44100; i++) |
1429 | fwrite(&pt->wavsample, 1, sizeof(s16), pt->ftape); |
1430 | le = i = ftell(pt->ftape); |
92064e2f |
1431 | #if ! CPU_IS_LE |
6d885935 |
1432 | le = (u8)(le>>24) | ((u8)le<<24) | ((u8)(le>>16)<<8) | ((u8)(le>>8)<<16); |
92064e2f |
1433 | #endif |
6d885935 |
1434 | fseek(pt->ftape, 4, SEEK_SET); |
1435 | fwrite(&le, 1, 4, pt->ftape); |
1436 | le = i-44; |
92064e2f |
1437 | #if ! CPU_IS_LE |
6d885935 |
1438 | le = (u8)(le>>24) | ((u8)le<<24) | ((u8)(le>>16)<<8) | ((u8)(le>>8)<<16); |
92064e2f |
1439 | #endif |
6d885935 |
1440 | fseek(pt->ftape, 40, SEEK_SET); |
1441 | fwrite(&le, 1, 4, pt->ftape); |
1442 | } else { |
1443 | pt->bitsample = ' '; |
1444 | for (i = 0; i < 1200; i++) |
1445 | fwrite(&pt->bitsample, 1, sizeof(u8), pt->ftape); |
1446 | } |
92064e2f |
1447 | } |
1448 | |
1449 | if (pt->ftape) fclose(pt->ftape); |
1450 | pt->ftape = NULL; |
1451 | } |
075672bf |
1452 | // vim:ts=2:sw=2:expandtab |