cff531af |
1 | /* |
2 | * SMS emulation |
3 | * (C) notaz, 2009-2010 |
4 | * |
5 | * This work is licensed under the terms of MAME license. |
6 | * See COPYING file in the top-level directory. |
7 | */ |
87b0845f |
8 | /* |
9 | * TODO: |
10 | * - start in a state as if BIOS ran |
87b0845f |
11 | * - RAM support in mapper |
12 | * - region support |
87b0845f |
13 | * - H counter |
14 | */ |
3e49ffd0 |
15 | #include "pico_int.h" |
af37bca8 |
16 | #include "memory.h" |
2ec9bec5 |
17 | #include "sound/sn76496.h" |
3e49ffd0 |
18 | |
2ec9bec5 |
19 | static unsigned char vdp_data_read(void) |
3e49ffd0 |
20 | { |
2ec9bec5 |
21 | struct PicoVideo *pv = &Pico.video; |
22 | unsigned char d; |
23 | |
88fd63ad |
24 | d = PicoMem.vramb[pv->addr]; |
2ec9bec5 |
25 | pv->addr = (pv->addr + 1) & 0x3fff; |
26 | pv->pending = 0; |
27 | return d; |
28 | } |
29 | |
30 | static unsigned char vdp_ctl_read(void) |
31 | { |
1c25c32c |
32 | struct PicoVideo *pv = &Pico.video; |
33 | unsigned char d; |
34 | |
cf83610b |
35 | z80_int_assert(0); |
1c25c32c |
36 | d = pv->status | (pv->pending_ints << 7); |
37 | pv->pending = pv->pending_ints = 0; |
38 | pv->status = 0; |
2ec9bec5 |
39 | |
40 | elprintf(EL_SR, "VDP sr: %02x", d); |
41 | return d; |
42 | } |
43 | |
44 | static void vdp_data_write(unsigned char d) |
45 | { |
46 | struct PicoVideo *pv = &Pico.video; |
47 | |
48 | if (pv->type == 3) { |
88fd63ad |
49 | PicoMem.cram[pv->addr & 0x1f] = d; |
200772b7 |
50 | Pico.m.dirtyPal = 1; |
2ec9bec5 |
51 | } else { |
88fd63ad |
52 | PicoMem.vramb[pv->addr] = d; |
2ec9bec5 |
53 | } |
54 | pv->addr = (pv->addr + 1) & 0x3fff; |
2ec9bec5 |
55 | |
56 | pv->pending = 0; |
57 | } |
58 | |
cf83610b |
59 | static NOINLINE void vdp_reg_write(struct PicoVideo *pv, u8 a, u8 d) |
60 | { |
61 | int l; |
62 | |
63 | pv->reg[a] = d; |
64 | switch (a) { |
65 | case 0: |
66 | l = pv->pending_ints & (d >> 3) & 2; |
67 | elprintf(EL_INTS, "hint %d", l); |
68 | z80_int_assert(l); |
69 | break; |
70 | case 1: |
71 | l = pv->pending_ints & (d >> 5) & 1; |
72 | elprintf(EL_INTS, "vint %d", l); |
73 | z80_int_assert(l); |
74 | break; |
75 | } |
76 | } |
77 | |
78 | static void vdp_ctl_write(u8 d) |
2ec9bec5 |
79 | { |
80 | struct PicoVideo *pv = &Pico.video; |
81 | |
82 | if (pv->pending) { |
83 | if ((d >> 6) == 2) { |
200772b7 |
84 | elprintf(EL_IO, " VDP r%02x=%02x", d & 0x0f, pv->addr & 0xff); |
cf83610b |
85 | if (pv->reg[d & 0x0f] != (u8)pv->addr) |
86 | vdp_reg_write(pv, d & 0x0f, pv->addr); |
2ec9bec5 |
87 | } |
88 | pv->type = d >> 6; |
89 | pv->addr &= 0x00ff; |
90 | pv->addr |= (d & 0x3f) << 8; |
91 | } else { |
92 | pv->addr &= 0x3f00; |
93 | pv->addr |= d; |
94 | } |
95 | pv->pending ^= 1; |
3e49ffd0 |
96 | } |
97 | |
2ec9bec5 |
98 | static unsigned char z80_sms_in(unsigned short a) |
3e49ffd0 |
99 | { |
2ec9bec5 |
100 | unsigned char d = 0; |
101 | |
200772b7 |
102 | elprintf(EL_IO, "z80 port %04x read", a); |
2ec9bec5 |
103 | a &= 0xc1; |
104 | switch (a) |
105 | { |
106 | case 0x00: |
107 | case 0x01: |
108 | d = 0xff; |
109 | break; |
110 | |
111 | case 0x40: /* V counter */ |
112 | d = Pico.video.v_counter; |
87b0845f |
113 | elprintf(EL_HVCNT, "V counter read: %02x", d); |
2ec9bec5 |
114 | break; |
115 | |
116 | case 0x41: /* H counter */ |
117 | d = Pico.m.rotate++; |
87b0845f |
118 | elprintf(EL_HVCNT, "H counter read: %02x", d); |
2ec9bec5 |
119 | break; |
120 | |
121 | case 0x80: |
122 | d = vdp_data_read(); |
123 | break; |
124 | |
125 | case 0x81: |
126 | d = vdp_ctl_read(); |
127 | break; |
128 | |
129 | case 0xc0: /* I/O port A and B */ |
93f9619e |
130 | d = ~((PicoIn.pad[0] & 0x3f) | (PicoIn.pad[1] << 6)); |
2ec9bec5 |
131 | break; |
132 | |
133 | case 0xc1: /* I/O port B and miscellaneous */ |
b4db550e |
134 | d = (Pico.ms.io_ctl & 0x80) | ((Pico.ms.io_ctl << 1) & 0x40) | 0x30; |
93f9619e |
135 | d |= ~(PicoIn.pad[1] >> 2) & 0x0f; |
2ec9bec5 |
136 | break; |
137 | } |
138 | |
200772b7 |
139 | elprintf(EL_IO, "ret = %02x", d); |
2ec9bec5 |
140 | return d; |
141 | } |
142 | |
143 | static void z80_sms_out(unsigned short a, unsigned char d) |
144 | { |
200772b7 |
145 | elprintf(EL_IO, "z80 port %04x write %02x", a, d); |
2ec9bec5 |
146 | a &= 0xc1; |
147 | switch (a) |
148 | { |
149 | case 0x01: |
b4db550e |
150 | Pico.ms.io_ctl = d; |
2ec9bec5 |
151 | break; |
152 | |
153 | case 0x40: |
154 | case 0x41: |
6311a3ba |
155 | if ((d & 0x90) == 0x90 && Pico.snd.psg_line < Pico.m.scanline) |
075672bf |
156 | PsndDoPSG(Pico.m.scanline); |
157 | SN76496Write(d); |
2ec9bec5 |
158 | break; |
159 | |
160 | case 0x80: |
161 | vdp_data_write(d); |
162 | break; |
163 | |
164 | case 0x81: |
165 | vdp_ctl_write(d); |
166 | break; |
167 | } |
168 | } |
169 | |
87b0845f |
170 | static int bank_mask; |
171 | |
2ec9bec5 |
172 | static void write_bank(unsigned short a, unsigned char d) |
173 | { |
460603fa |
174 | elprintf(EL_Z80BNK, "bank %04x %02x @ %04x", a, d, z80_pc()); |
2ec9bec5 |
175 | switch (a & 0x0f) |
176 | { |
177 | case 0x0c: |
87b0845f |
178 | elprintf(EL_STATUS|EL_ANOMALY, "%02x written to control reg!", d); |
2ec9bec5 |
179 | break; |
180 | case 0x0d: |
181 | if (d != 0) |
182 | elprintf(EL_STATUS|EL_ANOMALY, "bank0 changed to %d!", d); |
183 | break; |
184 | case 0x0e: |
87b0845f |
185 | d &= bank_mask; |
2ec9bec5 |
186 | z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0); |
187 | #ifdef _USE_CZ80 |
b8a1c09a |
188 | Cz80_Set_Fetch(&CZ80, 0x4000, 0x7fff, (FPTR)Pico.rom + (d << 14)); |
2ec9bec5 |
189 | #endif |
190 | break; |
191 | case 0x0f: |
87b0845f |
192 | d &= bank_mask; |
2ec9bec5 |
193 | z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); |
194 | #ifdef _USE_CZ80 |
b8a1c09a |
195 | Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.rom + (d << 14)); |
2ec9bec5 |
196 | #endif |
197 | break; |
198 | } |
b4db550e |
199 | Pico.ms.carthw[a & 0x0f] = d; |
3e49ffd0 |
200 | } |
201 | |
553c3eaa |
202 | static void xwrite(unsigned int a, unsigned char d) |
2ec9bec5 |
203 | { |
200772b7 |
204 | elprintf(EL_IO, "z80 write [%04x] %02x", a, d); |
2ec9bec5 |
205 | if (a >= 0xc000) |
88fd63ad |
206 | PicoMem.zram[a & 0x1fff] = d; |
b4db550e |
207 | if (a >= 0xfff8) |
2ec9bec5 |
208 | write_bank(a, d); |
3e49ffd0 |
209 | } |
210 | |
2ec9bec5 |
211 | void PicoResetMS(void) |
3e49ffd0 |
212 | { |
2ec9bec5 |
213 | z80_reset(); |
214 | PsndReset(); // pal must be known here |
3e49ffd0 |
215 | } |
216 | |
217 | void PicoPowerMS(void) |
218 | { |
87b0845f |
219 | int s, tmp; |
220 | |
88fd63ad |
221 | memset(&PicoMem,0,sizeof(PicoMem)); |
2ec9bec5 |
222 | memset(&Pico.video,0,sizeof(Pico.video)); |
223 | memset(&Pico.m,0,sizeof(Pico.m)); |
224 | Pico.m.pal = 0; |
225 | |
87b0845f |
226 | // calculate a mask for bank writes. |
227 | // ROM loader has aligned the size for us, so this is safe. |
228 | s = 0; tmp = Pico.romsize; |
229 | while ((tmp >>= 1) != 0) |
230 | s++; |
231 | if (Pico.romsize > (1 << s)) |
232 | s++; |
233 | tmp = 1 << s; |
234 | bank_mask = (tmp - 1) >> 14; |
235 | |
b4db550e |
236 | Pico.ms.carthw[0x0e] = 1; |
237 | Pico.ms.carthw[0x0f] = 2; |
238 | |
2ec9bec5 |
239 | PicoReset(); |
3e49ffd0 |
240 | } |
241 | |
242 | void PicoMemSetupMS(void) |
243 | { |
244 | z80_map_set(z80_read_map, 0x0000, 0xbfff, Pico.rom, 0); |
88fd63ad |
245 | z80_map_set(z80_read_map, 0xc000, 0xdfff, PicoMem.zram, 0); |
246 | z80_map_set(z80_read_map, 0xe000, 0xffff, PicoMem.zram, 0); |
3e49ffd0 |
247 | |
2ec9bec5 |
248 | z80_map_set(z80_write_map, 0x0000, 0xbfff, xwrite, 1); |
88fd63ad |
249 | z80_map_set(z80_write_map, 0xc000, 0xdfff, PicoMem.zram, 0); |
3e49ffd0 |
250 | z80_map_set(z80_write_map, 0xe000, 0xffff, xwrite, 1); |
251 | |
252 | #ifdef _USE_DRZ80 |
253 | drZ80.z80_in = z80_sms_in; |
254 | drZ80.z80_out = z80_sms_out; |
255 | #endif |
256 | #ifdef _USE_CZ80 |
b8a1c09a |
257 | Cz80_Set_Fetch(&CZ80, 0x0000, 0xbfff, (FPTR)Pico.rom); |
88fd63ad |
258 | Cz80_Set_Fetch(&CZ80, 0xc000, 0xdfff, (FPTR)PicoMem.zram); |
259 | Cz80_Set_Fetch(&CZ80, 0xe000, 0xffff, (FPTR)PicoMem.zram); |
3e49ffd0 |
260 | Cz80_Set_INPort(&CZ80, z80_sms_in); |
261 | Cz80_Set_OUTPort(&CZ80, z80_sms_out); |
262 | #endif |
263 | } |
264 | |
b4db550e |
265 | void PicoStateLoadedMS(void) |
266 | { |
267 | write_bank(0xfffe, Pico.ms.carthw[0x0e]); |
268 | write_bank(0xffff, Pico.ms.carthw[0x0f]); |
269 | } |
270 | |
3e49ffd0 |
271 | void PicoFrameMS(void) |
272 | { |
2ec9bec5 |
273 | struct PicoVideo *pv = &Pico.video; |
274 | int is_pal = Pico.m.pal; |
275 | int lines = is_pal ? 313 : 262; |
276 | int cycles_line = is_pal ? 58020 : 58293; /* (226.6 : 227.7) * 256 */ |
277 | int cycles_done = 0, cycles_aim = 0; |
93f9619e |
278 | int skip = PicoIn.skipFrame; |
2ec9bec5 |
279 | int lines_vis = 192; |
87b0845f |
280 | int hint; // Hint counter |
835122bc |
281 | int nmi; |
2ec9bec5 |
282 | int y; |
283 | |
4f2cdbf5 |
284 | PsndStartFrame(); |
285 | |
93f9619e |
286 | nmi = (PicoIn.pad[0] >> 7) & 1; |
835122bc |
287 | if (!Pico.ms.nmi_state && nmi) |
288 | z80_nmi(); |
289 | Pico.ms.nmi_state = nmi; |
290 | |
200772b7 |
291 | PicoFrameStartMode4(); |
87b0845f |
292 | hint = pv->reg[0x0a]; |
200772b7 |
293 | |
2ec9bec5 |
294 | for (y = 0; y < lines; y++) |
295 | { |
296 | pv->v_counter = Pico.m.scanline = y; |
03065bb6 |
297 | if (y > 218) |
298 | pv->v_counter = y - 6; |
2ec9bec5 |
299 | |
19954be1 |
300 | if (y < lines_vis && !skip) |
200772b7 |
301 | PicoLineMode4(y); |
87b0845f |
302 | |
303 | if (y <= lines_vis) |
304 | { |
305 | if (--hint < 0) |
306 | { |
307 | hint = pv->reg[0x0a]; |
308 | pv->pending_ints |= 2; |
309 | if (pv->reg[0] & 0x10) { |
310 | elprintf(EL_INTS, "hint"); |
cf83610b |
311 | z80_int_assert(1); |
87b0845f |
312 | } |
313 | } |
314 | } |
200772b7 |
315 | else if (y == lines_vis + 1) { |
87b0845f |
316 | pv->pending_ints |= 1; |
317 | if (pv->reg[1] & 0x20) { |
2ec9bec5 |
318 | elprintf(EL_INTS, "vint"); |
cf83610b |
319 | z80_int_assert(1); |
2ec9bec5 |
320 | } |
321 | } |
322 | |
075672bf |
323 | // 224 because of how it's done for MD... |
6311a3ba |
324 | if (y == 224 && PicoIn.sndOut) |
075672bf |
325 | PsndGetSamplesMS(); |
326 | |
2ec9bec5 |
327 | cycles_aim += cycles_line; |
328 | cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8; |
329 | } |
330 | |
6311a3ba |
331 | if (PicoIn.sndOut && Pico.snd.psg_line < lines) |
075672bf |
332 | PsndDoPSG(lines - 1); |
3e49ffd0 |
333 | } |
334 | |
87b0845f |
335 | void PicoFrameDrawOnlyMS(void) |
336 | { |
337 | int lines_vis = 192; |
338 | int y; |
339 | |
340 | PicoFrameStartMode4(); |
341 | |
342 | for (y = 0; y < lines_vis; y++) |
343 | PicoLineMode4(y); |
344 | } |
345 | |
075672bf |
346 | // vim:ts=2:sw=2:expandtab |