Mercurial > repos > blastem
comparison genesis.c @ 1103:22e87b739ad6
WIP split of ROM loading/argument parsing from Genesis emulation code. Compiles and doesn't crash, but nothing works. Still a few too many globals as well.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 09 Dec 2016 09:48:48 -0800 |
parents | |
children | 4224980a5f84 |
comparison
equal
deleted
inserted
replaced
1102:c15896605bf2 | 1103:22e87b739ad6 |
---|---|
1 /* | |
2 Copyright 2013-2016 Michael Pavone | |
3 This file is part of BlastEm. | |
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. | |
5 */ | |
6 #include "genesis.h" | |
7 #include "blastem.h" | |
8 #include <stdlib.h> | |
9 #include "render.h" | |
10 #include "gst.h" | |
11 #include "util.h" | |
12 #define MCLKS_NTSC 53693175 | |
13 #define MCLKS_PAL 53203395 | |
14 | |
15 uint32_t MCLKS_PER_68K; | |
16 #define MCLKS_PER_YM 7 | |
17 #define MCLKS_PER_Z80 15 | |
18 #define MCLKS_PER_PSG (MCLKS_PER_Z80*16) | |
19 #define DEFAULT_SYNC_INTERVAL MCLKS_LINE | |
20 #define DEFAULT_LOWPASS_CUTOFF 3390 | |
21 | |
22 //TODO: Figure out the exact value for this | |
23 #define LINES_NTSC 262 | |
24 #define LINES_PAL 312 | |
25 | |
26 #define MAX_SOUND_CYCLES 100000 | |
27 | |
28 uint16_t *cart; | |
29 uint16_t *ram; | |
30 uint8_t z80_ram[Z80_RAM_BYTES]; | |
31 | |
32 uint16_t read_dma_value(uint32_t address) | |
33 { | |
34 //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2 | |
35 uint16_t *ptr = get_native_pointer(address*2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen); | |
36 if (ptr) { | |
37 return *ptr; | |
38 } | |
39 //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area | |
40 return 0; | |
41 } | |
42 | |
43 uint16_t get_open_bus_value() | |
44 { | |
45 return read_dma_value(genesis->m68k->last_prefetch_address/2); | |
46 } | |
47 | |
48 void adjust_int_cycle(m68k_context * context, vdp_context * v_context) | |
49 { | |
50 //static int old_int_cycle = CYCLE_NEVER; | |
51 genesis_context *gen = context->system; | |
52 if (context->sync_cycle - context->current_cycle > gen->max_cycles) { | |
53 context->sync_cycle = context->current_cycle + gen->max_cycles; | |
54 } | |
55 context->int_cycle = CYCLE_NEVER; | |
56 if ((context->status & 0x7) < 6) { | |
57 uint32_t next_vint = vdp_next_vint(v_context); | |
58 if (next_vint != CYCLE_NEVER) { | |
59 context->int_cycle = next_vint; | |
60 context->int_num = 6; | |
61 } | |
62 if ((context->status & 0x7) < 4) { | |
63 uint32_t next_hint = vdp_next_hint(v_context); | |
64 if (next_hint != CYCLE_NEVER) { | |
65 next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; | |
66 if (next_hint < context->int_cycle) { | |
67 context->int_cycle = next_hint; | |
68 context->int_num = 4; | |
69 | |
70 } | |
71 } | |
72 } | |
73 } | |
74 if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { | |
75 context->int_pending = INT_PENDING_NONE; | |
76 } | |
77 /*if (context->int_cycle != old_int_cycle) { | |
78 printf("int cycle changed to: %d, level: %d @ %d(%d), frame: %d, vcounter: %d, hslot: %d, mask: %d, hint_counter: %d\n", context->int_cycle, context->int_num, v_context->cycles, context->current_cycle, v_context->frame, v_context->vcounter, v_context->hslot, context->status & 0x7, v_context->hint_counter); | |
79 old_int_cycle = context->int_cycle; | |
80 }*/ | |
81 | |
82 context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; | |
83 if (context->should_return) { | |
84 context->target_cycle = context->current_cycle; | |
85 } else if (context->target_cycle < context->current_cycle) { | |
86 //Changes to SR can result in an interrupt cycle that's in the past | |
87 //This can cause issues with the implementation of STOP though | |
88 context->target_cycle = context->current_cycle; | |
89 } | |
90 /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", | |
91 context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), | |
92 v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/ | |
93 } | |
94 | |
95 //#define DO_DEBUG_PRINT | |
96 #ifdef DO_DEBUG_PRINT | |
97 #define dprintf printf | |
98 #define dputs puts | |
99 #else | |
100 #define dprintf | |
101 #define dputs | |
102 #endif | |
103 | |
104 #define Z80_VINT_DURATION 128 | |
105 | |
106 void z80_next_int_pulse(z80_context * z_context) | |
107 { | |
108 genesis_context * gen = z_context->system; | |
109 z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp); | |
110 z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80; | |
111 } | |
112 | |
113 void sync_z80(z80_context * z_context, uint32_t mclks) | |
114 { | |
115 #ifndef NO_Z80 | |
116 if (z80_enabled) { | |
117 z80_run(z_context, mclks); | |
118 } else | |
119 #endif | |
120 { | |
121 z_context->current_cycle = mclks; | |
122 } | |
123 } | |
124 | |
125 void sync_sound(genesis_context * gen, uint32_t target) | |
126 { | |
127 //printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2); | |
128 while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { | |
129 uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; | |
130 //printf("Running PSG to cycle %d\n", cur_target); | |
131 psg_run(gen->psg, cur_target); | |
132 //printf("Running YM-2612 to cycle %d\n", cur_target); | |
133 ym_run(gen->ym, cur_target); | |
134 } | |
135 psg_run(gen->psg, target); | |
136 ym_run(gen->ym, target); | |
137 | |
138 //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); | |
139 } | |
140 | |
141 uint32_t last_frame_num; | |
142 | |
143 //My refresh emulation isn't currently good enough and causes more problems than it solves | |
144 #ifdef REFRESH_EMULATION | |
145 #define REFRESH_INTERVAL 128 | |
146 #define REFRESH_DELAY 2 | |
147 uint32_t last_sync_cycle; | |
148 uint32_t refresh_counter; | |
149 #endif | |
150 | |
151 m68k_context * sync_components(m68k_context * context, uint32_t address) | |
152 { | |
153 genesis_context * gen = context->system; | |
154 vdp_context * v_context = gen->vdp; | |
155 z80_context * z_context = gen->z80; | |
156 #ifdef REFRESH_EMULATION | |
157 //lame estimation of refresh cycle delay | |
158 if (!gen->bus_busy) { | |
159 refresh_counter += context->current_cycle - last_sync_cycle; | |
160 context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); | |
161 refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); | |
162 } | |
163 #endif | |
164 | |
165 uint32_t mclks = context->current_cycle; | |
166 sync_z80(z_context, mclks); | |
167 sync_sound(gen, mclks); | |
168 vdp_run_context(v_context, mclks); | |
169 if (v_context->frame != last_frame_num) { | |
170 //printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", last_frame_num, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot); | |
171 last_frame_num = v_context->frame; | |
172 | |
173 if(exit_after){ | |
174 --exit_after; | |
175 if (!exit_after) { | |
176 exit(0); | |
177 } | |
178 } | |
179 | |
180 vdp_adjust_cycles(v_context, mclks); | |
181 io_adjust_cycles(gen->ports, context->current_cycle, mclks); | |
182 io_adjust_cycles(gen->ports+1, context->current_cycle, mclks); | |
183 io_adjust_cycles(gen->ports+2, context->current_cycle, mclks); | |
184 context->current_cycle -= mclks; | |
185 z80_adjust_cycles(z_context, mclks); | |
186 gen->ym->current_cycle -= mclks; | |
187 gen->psg->cycles -= mclks; | |
188 if (gen->ym->write_cycle != CYCLE_NEVER) { | |
189 gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0; | |
190 } | |
191 } | |
192 gen->frame_end = vdp_cycles_to_frame_end(v_context); | |
193 context->sync_cycle = gen->frame_end; | |
194 //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); | |
195 if (context->int_ack) { | |
196 //printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot); | |
197 vdp_int_ack(v_context); | |
198 context->int_ack = 0; | |
199 } | |
200 if (!address && (break_on_sync || gen->save_state)) { | |
201 context->sync_cycle = context->current_cycle + 1; | |
202 } | |
203 adjust_int_cycle(context, v_context); | |
204 if (address) { | |
205 if (break_on_sync) { | |
206 break_on_sync = 0; | |
207 debugger(context, address); | |
208 } | |
209 if (gen->save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) { | |
210 uint8_t slot = gen->save_state - 1; | |
211 gen->save_state = 0; | |
212 //advance Z80 core to the start of an instruction | |
213 while (!z_context->pc) | |
214 { | |
215 sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80); | |
216 } | |
217 char *save_path; | |
218 if (slot == QUICK_SAVE_SLOT) { | |
219 save_path = save_state_path; | |
220 } else { | |
221 char slotname[] = "slot_0.gst"; | |
222 slotname[5] = '0' + slot; | |
223 char const *parts[] = {gen->save_dir, PATH_SEP, slotname}; | |
224 save_path = alloc_concat_m(3, parts); | |
225 } | |
226 save_gst(gen, save_path, address); | |
227 printf("Saved state to %s\n", save_path); | |
228 if (slot != QUICK_SAVE_SLOT) { | |
229 free(save_path); | |
230 } | |
231 } else if(gen->save_state) { | |
232 context->sync_cycle = context->current_cycle + 1; | |
233 } | |
234 } | |
235 #ifdef REFRESH_EMULATION | |
236 last_sync_cycle = context->current_cycle; | |
237 #endif | |
238 return context; | |
239 } | |
240 | |
241 m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) | |
242 { | |
243 if (vdp_port & 0x2700E0) { | |
244 fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port); | |
245 } | |
246 vdp_port &= 0x1F; | |
247 //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); | |
248 sync_components(context, 0); | |
249 genesis_context * gen = context->system; | |
250 vdp_context *v_context = gen->vdp; | |
251 if (vdp_port < 0x10) { | |
252 int blocked; | |
253 uint32_t before_cycle = v_context->cycles; | |
254 if (vdp_port < 4) { | |
255 | |
256 while (vdp_data_port_write(v_context, value) < 0) { | |
257 while(v_context->flags & FLAG_DMA_RUN) { | |
258 vdp_run_dma_done(v_context, gen->frame_end); | |
259 if (v_context->cycles >= gen->frame_end) { | |
260 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
261 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
262 if (m68k_cycle_diff < cycle_diff) { | |
263 m68k_cycle_diff += MCLKS_PER_68K; | |
264 } | |
265 context->current_cycle += m68k_cycle_diff; | |
266 gen->bus_busy = 1; | |
267 sync_components(context, 0); | |
268 gen->bus_busy = 0; | |
269 } | |
270 } | |
271 //context->current_cycle = v_context->cycles; | |
272 } | |
273 } else if(vdp_port < 8) { | |
274 blocked = vdp_control_port_write(v_context, value); | |
275 if (blocked) { | |
276 while (blocked) { | |
277 while(v_context->flags & FLAG_DMA_RUN) { | |
278 vdp_run_dma_done(v_context, gen->frame_end); | |
279 if (v_context->cycles >= gen->frame_end) { | |
280 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
281 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
282 if (m68k_cycle_diff < cycle_diff) { | |
283 m68k_cycle_diff += MCLKS_PER_68K; | |
284 } | |
285 context->current_cycle += m68k_cycle_diff; | |
286 gen->bus_busy = 1; | |
287 sync_components(context, 0); | |
288 gen->bus_busy = 0; | |
289 } | |
290 if (!(v_context->flags & FLAG_DMA_RUN)) { | |
291 //two more slots of delay are needed to kill sufficient sprite capacity in Overdrive | |
292 //TODO: Measure exact value with logic analyzer | |
293 vdp_run_context(v_context, v_context->cycles + 1); | |
294 vdp_run_context(v_context, v_context->cycles + 1); | |
295 } | |
296 } | |
297 | |
298 if (blocked < 0) { | |
299 blocked = vdp_control_port_write(v_context, value); | |
300 } else { | |
301 blocked = 0; | |
302 } | |
303 } | |
304 } else { | |
305 context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context); | |
306 //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); | |
307 adjust_int_cycle(context, v_context); | |
308 } | |
309 } else { | |
310 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
311 } | |
312 if (v_context->cycles != before_cycle) { | |
313 //printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); | |
314 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
315 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
316 if (m68k_cycle_diff < cycle_diff) { | |
317 m68k_cycle_diff += MCLKS_PER_68K; | |
318 } | |
319 context->current_cycle += m68k_cycle_diff; | |
320 #ifdef REFRESH_EMULATION | |
321 last_sync_cycle = context->current_cycle; | |
322 #endif | |
323 //Lock the Z80 out of the bus until the VDP access is complete | |
324 gen->bus_busy = 1; | |
325 sync_z80(gen->z80, v_context->cycles); | |
326 gen->bus_busy = 0; | |
327 } | |
328 } else if (vdp_port < 0x18) { | |
329 psg_write(gen->psg, value); | |
330 } else { | |
331 //TODO: Implement undocumented test register(s) | |
332 } | |
333 return context; | |
334 } | |
335 | |
336 m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value) | |
337 { | |
338 return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0)); | |
339 } | |
340 | |
341 void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value) | |
342 { | |
343 z80_context * context = vcontext; | |
344 genesis_context * gen = context->system; | |
345 vdp_port &= 0xFF; | |
346 if (vdp_port & 0xE0) { | |
347 fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port); | |
348 } | |
349 if (vdp_port < 0x10) { | |
350 //These probably won't currently interact well with the 68K accessing the VDP | |
351 vdp_run_context(gen->vdp, context->current_cycle); | |
352 if (vdp_port < 4) { | |
353 vdp_data_port_write(gen->vdp, value << 8 | value); | |
354 } else if (vdp_port < 8) { | |
355 vdp_control_port_write(gen->vdp, value << 8 | value); | |
356 } else { | |
357 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
358 } | |
359 } else if (vdp_port < 0x18) { | |
360 sync_sound(gen, context->current_cycle); | |
361 psg_write(gen->psg, value); | |
362 } else { | |
363 vdp_test_port_write(gen->vdp, value); | |
364 } | |
365 return context; | |
366 } | |
367 | |
368 uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) | |
369 { | |
370 if (vdp_port & 0x2700E0) { | |
371 fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port); | |
372 } | |
373 vdp_port &= 0x1F; | |
374 uint16_t value; | |
375 sync_components(context, 0); | |
376 genesis_context *gen = context->system; | |
377 vdp_context * v_context = gen->vdp; | |
378 uint32_t before_cycle = v_context->cycles; | |
379 if (vdp_port < 0x10) { | |
380 if (vdp_port < 4) { | |
381 value = vdp_data_port_read(v_context); | |
382 } else if(vdp_port < 8) { | |
383 value = vdp_control_port_read(v_context); | |
384 } else { | |
385 value = vdp_hv_counter_read(v_context); | |
386 //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); | |
387 } | |
388 } else if (vdp_port < 0x18){ | |
389 fatal_error("Illegal read from PSG port %X\n", vdp_port); | |
390 } else { | |
391 value = vdp_test_port_read(v_context); | |
392 } | |
393 if (v_context->cycles != before_cycle) { | |
394 //printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); | |
395 context->current_cycle = v_context->cycles; | |
396 #ifdef REFRES_EMULATION | |
397 last_sync_cycle = context->current_cycle; | |
398 #endif | |
399 //Lock the Z80 out of the bus until the VDP access is complete | |
400 genesis_context *gen = context->system; | |
401 gen->bus_busy = 1; | |
402 sync_z80(gen->z80, v_context->cycles); | |
403 gen->bus_busy = 0; | |
404 } | |
405 return value; | |
406 } | |
407 | |
408 uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context) | |
409 { | |
410 uint16_t value = vdp_port_read(vdp_port, context); | |
411 if (vdp_port & 1) { | |
412 return value; | |
413 } else { | |
414 return value >> 8; | |
415 } | |
416 } | |
417 | |
418 uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext) | |
419 { | |
420 z80_context * context = vcontext; | |
421 if (vdp_port & 0xE0) { | |
422 fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port); | |
423 } | |
424 genesis_context * gen = context->system; | |
425 //VDP access goes over the 68K bus like a bank area access | |
426 //typical delay from bus arbitration | |
427 context->current_cycle += 3 * MCLKS_PER_Z80; | |
428 //TODO: add cycle for an access right after a previous one | |
429 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
430 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
431 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
432 | |
433 | |
434 vdp_port &= 0x1F; | |
435 uint16_t ret; | |
436 if (vdp_port < 0x10) { | |
437 //These probably won't currently interact well with the 68K accessing the VDP | |
438 vdp_run_context(gen->vdp, context->current_cycle); | |
439 if (vdp_port < 4) { | |
440 ret = vdp_data_port_read(gen->vdp); | |
441 } else if (vdp_port < 8) { | |
442 ret = vdp_control_port_read(gen->vdp); | |
443 } else { | |
444 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
445 } | |
446 } else { | |
447 //TODO: Figure out the correct value today | |
448 ret = 0xFFFF; | |
449 } | |
450 return vdp_port & 1 ? ret : ret >> 8; | |
451 } | |
452 | |
453 uint32_t zram_counter = 0; | |
454 | |
455 m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) | |
456 { | |
457 genesis_context * gen = context->system; | |
458 if (location < 0x10000) { | |
459 //Access to Z80 memory incurs a one 68K cycle wait state | |
460 context->current_cycle += MCLKS_PER_68K; | |
461 if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { | |
462 location &= 0x7FFF; | |
463 if (location < 0x4000) { | |
464 z80_ram[location & 0x1FFF] = value; | |
465 #ifndef NO_Z80 | |
466 z80_handle_code_write(location & 0x1FFF, gen->z80); | |
467 #endif | |
468 } else if (location < 0x6000) { | |
469 sync_sound(gen, context->current_cycle); | |
470 if (location & 1) { | |
471 ym_data_write(gen->ym, value); | |
472 } else if(location & 2) { | |
473 ym_address_write_part2(gen->ym, value); | |
474 } else { | |
475 ym_address_write_part1(gen->ym, value); | |
476 } | |
477 } else if (location == 0x6000) { | |
478 gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF; | |
479 if (gen->z80->bank_reg < 0x80) { | |
480 gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]); | |
481 } else { | |
482 gen->z80->mem_pointers[1] = NULL; | |
483 } | |
484 } else { | |
485 fatal_error("68K write to unhandled Z80 address %X\n", location); | |
486 } | |
487 } | |
488 } else { | |
489 location &= 0x1FFF; | |
490 if (location < 0x100) { | |
491 switch(location/2) | |
492 { | |
493 case 0x1: | |
494 io_data_write(gen->ports, value, context->current_cycle); | |
495 break; | |
496 case 0x2: | |
497 io_data_write(gen->ports+1, value, context->current_cycle); | |
498 break; | |
499 case 0x3: | |
500 io_data_write(gen->ports+2, value, context->current_cycle); | |
501 break; | |
502 case 0x4: | |
503 gen->ports[0].control = value; | |
504 break; | |
505 case 0x5: | |
506 gen->ports[1].control = value; | |
507 break; | |
508 case 0x6: | |
509 gen->ports[2].control = value; | |
510 break; | |
511 } | |
512 } else { | |
513 if (location == 0x1100) { | |
514 if (value & 1) { | |
515 dputs("bus requesting Z80"); | |
516 if (z80_enabled) { | |
517 z80_assert_busreq(gen->z80, context->current_cycle); | |
518 } else { | |
519 gen->z80->busack = 1; | |
520 } | |
521 } else { | |
522 if (gen->z80->busreq) { | |
523 dputs("releasing z80 bus"); | |
524 #ifdef DO_DEBUG_PRINT | |
525 char fname[20]; | |
526 sprintf(fname, "zram-%d", zram_counter++); | |
527 FILE * f = fopen(fname, "wb"); | |
528 fwrite(z80_ram, 1, sizeof(z80_ram), f); | |
529 fclose(f); | |
530 #endif | |
531 } | |
532 if (z80_enabled) { | |
533 z80_clear_busreq(gen->z80, context->current_cycle); | |
534 } else { | |
535 gen->z80->busack = 0; | |
536 } | |
537 } | |
538 } else if (location == 0x1200) { | |
539 sync_z80(gen->z80, context->current_cycle); | |
540 if (value & 1) { | |
541 if (z80_enabled) { | |
542 z80_clear_reset(gen->z80, context->current_cycle); | |
543 } else { | |
544 gen->z80->reset = 0; | |
545 } | |
546 } else { | |
547 if (z80_enabled) { | |
548 z80_assert_reset(gen->z80, context->current_cycle); | |
549 } else { | |
550 gen->z80->reset = 1; | |
551 } | |
552 } | |
553 } | |
554 } | |
555 } | |
556 return context; | |
557 } | |
558 | |
559 m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) | |
560 { | |
561 if (location < 0x10000 || (location & 0x1FFF) >= 0x100) { | |
562 return io_write(location, context, value >> 8); | |
563 } else { | |
564 return io_write(location, context, value); | |
565 } | |
566 } | |
567 | |
568 #define FOREIGN 0x80 | |
569 #define HZ50 0x40 | |
570 #define USA FOREIGN | |
571 #define JAP 0x00 | |
572 #define EUR (HZ50|FOREIGN) | |
573 #define NO_DISK 0x20 | |
574 | |
575 uint8_t io_read(uint32_t location, m68k_context * context) | |
576 { | |
577 uint8_t value; | |
578 genesis_context *gen = context->system; | |
579 if (location < 0x10000) { | |
580 //Access to Z80 memory incurs a one 68K cycle wait state | |
581 context->current_cycle += MCLKS_PER_68K; | |
582 if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { | |
583 location &= 0x7FFF; | |
584 if (location < 0x4000) { | |
585 value = z80_ram[location & 0x1FFF]; | |
586 } else if (location < 0x6000) { | |
587 sync_sound(gen, context->current_cycle); | |
588 value = ym_read_status(gen->ym); | |
589 } else { | |
590 value = 0xFF; | |
591 } | |
592 } else { | |
593 value = 0xFF; | |
594 } | |
595 } else { | |
596 location &= 0x1FFF; | |
597 if (location < 0x100) { | |
598 switch(location/2) | |
599 { | |
600 case 0x0: | |
601 //version bits should be 0 for now since we're not emulating TMSS | |
602 value = gen->version_reg; | |
603 break; | |
604 case 0x1: | |
605 value = io_data_read(gen->ports, context->current_cycle); | |
606 break; | |
607 case 0x2: | |
608 value = io_data_read(gen->ports+1, context->current_cycle); | |
609 break; | |
610 case 0x3: | |
611 value = io_data_read(gen->ports+2, context->current_cycle); | |
612 break; | |
613 case 0x4: | |
614 value = gen->ports[0].control; | |
615 break; | |
616 case 0x5: | |
617 value = gen->ports[1].control; | |
618 break; | |
619 case 0x6: | |
620 value = gen->ports[2].control; | |
621 break; | |
622 default: | |
623 value = 0xFF; | |
624 } | |
625 } else { | |
626 if (location == 0x1100) { | |
627 value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack; | |
628 value |= (get_open_bus_value() >> 8) & 0xFE; | |
629 dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset); | |
630 } else if (location == 0x1200) { | |
631 value = !gen->z80->reset; | |
632 } else { | |
633 value = 0xFF; | |
634 printf("Byte read of unknown IO location: %X\n", location); | |
635 } | |
636 } | |
637 } | |
638 return value; | |
639 } | |
640 | |
641 uint16_t io_read_w(uint32_t location, m68k_context * context) | |
642 { | |
643 uint16_t value = io_read(location, context); | |
644 if (location < 0x10000 || (location & 0x1FFF) < 0x100) { | |
645 value = value | (value << 8); | |
646 } else { | |
647 value <<= 8; | |
648 value |= get_open_bus_value() & 0xFF; | |
649 } | |
650 return value; | |
651 } | |
652 | |
653 void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value) | |
654 { | |
655 z80_context * context = vcontext; | |
656 genesis_context * gen = context->system; | |
657 sync_sound(gen, context->current_cycle); | |
658 if (location & 1) { | |
659 ym_data_write(gen->ym, value); | |
660 } else if (location & 2) { | |
661 ym_address_write_part2(gen->ym, value); | |
662 } else { | |
663 ym_address_write_part1(gen->ym, value); | |
664 } | |
665 return context; | |
666 } | |
667 | |
668 uint8_t z80_read_ym(uint32_t location, void * vcontext) | |
669 { | |
670 z80_context * context = vcontext; | |
671 genesis_context * gen = context->system; | |
672 sync_sound(gen, context->current_cycle); | |
673 return ym_read_status(gen->ym); | |
674 } | |
675 | |
676 uint8_t z80_read_bank(uint32_t location, void * vcontext) | |
677 { | |
678 z80_context * context = vcontext; | |
679 genesis_context *gen = context->system; | |
680 if (gen->bus_busy) { | |
681 context->current_cycle = context->sync_cycle; | |
682 } | |
683 //typical delay from bus arbitration | |
684 context->current_cycle += 3 * MCLKS_PER_Z80; | |
685 //TODO: add cycle for an access right after a previous one | |
686 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
687 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
688 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
689 | |
690 location &= 0x7FFF; | |
691 if (context->mem_pointers[1]) { | |
692 return context->mem_pointers[1][location ^ 1]; | |
693 } | |
694 uint32_t address = context->bank_reg << 15 | location; | |
695 if (address >= 0xC00000 && address < 0xE00000) { | |
696 return z80_vdp_port_read(location & 0xFF, context); | |
697 } else { | |
698 fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15); | |
699 } | |
700 return 0; | |
701 } | |
702 | |
703 void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value) | |
704 { | |
705 z80_context * context = vcontext; | |
706 genesis_context *gen = context->system; | |
707 if (gen->bus_busy) { | |
708 context->current_cycle = context->sync_cycle; | |
709 } | |
710 //typical delay from bus arbitration | |
711 context->current_cycle += 3 * MCLKS_PER_Z80; | |
712 //TODO: add cycle for an access right after a previous one | |
713 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
714 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
715 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
716 | |
717 location &= 0x7FFF; | |
718 uint32_t address = context->bank_reg << 15 | location; | |
719 if (address >= 0xE00000) { | |
720 address &= 0xFFFF; | |
721 ((uint8_t *)ram)[address ^ 1] = value; | |
722 } else if (address >= 0xC00000) { | |
723 z80_vdp_port_write(location & 0xFF, context, value); | |
724 } else { | |
725 fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address); | |
726 } | |
727 return context; | |
728 } | |
729 | |
730 void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value) | |
731 { | |
732 z80_context * context = vcontext; | |
733 | |
734 context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF; | |
735 if (context->bank_reg < 0x100) { | |
736 genesis_context *gen = context->system; | |
737 context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen); | |
738 } else { | |
739 context->mem_pointers[1] = NULL; | |
740 } | |
741 | |
742 return context; | |
743 } | |
744 | |
745 void set_speed_percent(genesis_context * context, uint32_t percent) | |
746 { | |
747 uint32_t old_clock = context->master_clock; | |
748 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; | |
749 while (context->ym->current_cycle != context->psg->cycles) { | |
750 sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); | |
751 } | |
752 ym_adjust_master_clock(context->ym, context->master_clock); | |
753 psg_adjust_master_clock(context->psg, context->master_clock); | |
754 } | |
755 | |
756 void set_region(genesis_context *gen, rom_info *info, uint8_t region) | |
757 { | |
758 if (!region) { | |
759 char * def_region = tern_find_ptr(config, "default_region"); | |
760 if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) { | |
761 region = translate_region_char(toupper(*def_region)); | |
762 } else { | |
763 region = info->regions; | |
764 } | |
765 } | |
766 if (region & REGION_E) { | |
767 gen->version_reg = NO_DISK | EUR; | |
768 } else if (region & REGION_J) { | |
769 gen->version_reg = NO_DISK | JAP; | |
770 } else { | |
771 gen->version_reg = NO_DISK | USA; | |
772 } | |
773 | |
774 if (region & HZ50) { | |
775 gen->normal_clock = MCLKS_PAL; | |
776 } else { | |
777 gen->normal_clock = MCLKS_NTSC; | |
778 } | |
779 gen->master_clock = gen->normal_clock; | |
780 } | |
781 | |
782 genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t ym_opts, uint8_t force_region) | |
783 { | |
784 static memmap_chunk z80_map[] = { | |
785 { 0x0000, 0x4000, 0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL, NULL, NULL, NULL, NULL }, | |
786 { 0x8000, 0x10000, 0x7FFF, 0, 0, 0, NULL, NULL, NULL, z80_read_bank, z80_write_bank}, | |
787 { 0x4000, 0x6000, 0x0003, 0, 0, 0, NULL, NULL, NULL, z80_read_ym, z80_write_ym}, | |
788 { 0x6000, 0x6100, 0xFFFF, 0, 0, 0, NULL, NULL, NULL, NULL, z80_write_bank_reg}, | |
789 { 0x7F00, 0x8000, 0x00FF, 0, 0, 0, NULL, NULL, NULL, z80_vdp_port_read, z80_vdp_port_write} | |
790 }; | |
791 genesis_context *gen = calloc(1, sizeof(genesis_context)); | |
792 set_region(gen, rom, force_region); | |
793 | |
794 gen->vdp = malloc(sizeof(vdp_context)); | |
795 init_vdp_context(gen->vdp, gen->version_reg & 0x40); | |
796 gen->frame_end = vdp_cycles_to_frame_end(gen->vdp); | |
797 char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval; | |
798 gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL; | |
799 | |
800 char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval; | |
801 uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF; | |
802 | |
803 gen->ym = malloc(sizeof(ym2612_context)); | |
804 ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff); | |
805 | |
806 gen->psg = malloc(sizeof(psg_context)); | |
807 psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff); | |
808 | |
809 gen->z80 = calloc(1, sizeof(z80_context)); | |
810 z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES); | |
811 #ifndef NO_Z80 | |
812 z80_options *z_opts = malloc(sizeof(z80_options)); | |
813 init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80); | |
814 init_z80_context(gen->z80, z_opts); | |
815 z80_assert_reset(gen->z80, 0); | |
816 #endif | |
817 | |
818 gen->z80->system = gen; | |
819 gen->z80->mem_pointers[0] = gen->zram; | |
820 gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart; | |
821 | |
822 gen->cart = main_rom; | |
823 gen->lock_on = lock_on; | |
824 gen->work_ram = calloc(2, RAM_WORDS); | |
825 gen->zram = z80_ram; | |
826 setup_io_devices(config, rom, gen); | |
827 | |
828 gen->save_type = rom->save_type; | |
829 gen->save_type = rom->save_type; | |
830 if (gen->save_type != SAVE_NONE) { | |
831 gen->save_ram_mask = rom->save_mask; | |
832 gen->save_size = rom->save_size; | |
833 gen->save_storage = rom->save_buffer; | |
834 gen->eeprom_map = rom->eeprom_map; | |
835 gen->num_eeprom = rom->num_eeprom; | |
836 if (gen->save_type == SAVE_I2C) { | |
837 eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); | |
838 } | |
839 } else { | |
840 gen->save_storage = NULL; | |
841 } | |
842 | |
843 for (int i = 0; i < rom->map_chunks; i++) | |
844 { | |
845 if (rom->map[i].flags & MMAP_PTR_IDX) { | |
846 gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; | |
847 } | |
848 if (rom->map[i].start == 0xE00000) { | |
849 rom->map[i].buffer = gen->work_ram; | |
850 } | |
851 } | |
852 | |
853 m68k_options *opts = malloc(sizeof(m68k_options)); | |
854 init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K); | |
855 //TODO: make this configurable | |
856 opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; | |
857 gen->m68k = init_68k_context(opts, NULL); | |
858 gen->m68k->system = gen; | |
859 | |
860 return gen; | |
861 } | |
862 | |
863 | |
864 | |
865 void free_genesis(genesis_context *gen) | |
866 { | |
867 vdp_free(gen->vdp); | |
868 m68k_options_free(gen->m68k->options); | |
869 free(gen->m68k); | |
870 free(gen->work_ram); | |
871 z80_options_free(gen->z80->options); | |
872 free(gen->z80); | |
873 free(gen->zram); | |
874 ym_free(gen->ym); | |
875 psg_free(gen->psg); | |
876 free(gen->save_storage); | |
877 free(gen->save_dir); | |
878 free(gen->lock_on); | |
879 } | |
880 | |
881 void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger) | |
882 { | |
883 | |
884 if (statefile) { | |
885 uint32_t pc = load_gst(gen, statefile); | |
886 if (!pc) { | |
887 fatal_error("Failed to load save state %s\n", statefile); | |
888 } | |
889 printf("Loaded %s\n", statefile); | |
890 if (debugger) { | |
891 insert_breakpoint(gen->m68k, pc, debugger); | |
892 } | |
893 adjust_int_cycle(gen->m68k, gen->vdp); | |
894 start_68k_context(gen->m68k, pc); | |
895 } else { | |
896 if (debugger) { | |
897 uint32_t address = cart[2] << 16 | cart[3]; | |
898 insert_breakpoint(gen->m68k, address, debugger); | |
899 } | |
900 m68k_reset(gen->m68k); | |
901 } | |
902 } | |
903 | |
904 genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out) | |
905 { | |
906 static memmap_chunk base_map[] = { | |
907 {0xE00000, 0x1000000, 0xFFFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL, | |
908 NULL, NULL, NULL, NULL}, | |
909 {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, 0, NULL, | |
910 (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, | |
911 (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, | |
912 {0xA00000, 0xA12000, 0x1FFFF, 0, 0, 0, NULL, | |
913 (read_16_fun)io_read_w, (write_16_fun)io_write_w, | |
914 (read_8_fun)io_read, (write_8_fun)io_write} | |
915 }; | |
916 static tern_node *rom_db; | |
917 if (!rom_db) { | |
918 rom_db = load_rom_db(); | |
919 } | |
920 *info_out = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0])); | |
921 #ifndef BIG_ENDIAN | |
922 byteswap_rom(rom, rom_size); | |
923 if (lock_on) { | |
924 byteswap_rom(lock_on, lock_on_size); | |
925 } | |
926 #endif | |
927 char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval; | |
928 if (!m68k_divider) { | |
929 m68k_divider = "7"; | |
930 } | |
931 MCLKS_PER_68K = atoi(m68k_divider); | |
932 if (!MCLKS_PER_68K) { | |
933 MCLKS_PER_68K = 7; | |
934 } | |
935 return alloc_init_genesis(info_out, rom, lock_on, ym_opts, force_region); | |
936 } |