Mercurial > repos > blastem
comparison blastem.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 | faa3a4617f62 |
children | 2eb54e24914e |
comparison
equal
deleted
inserted
replaced
1102:c15896605bf2 | 1103:22e87b739ad6 |
---|---|
12 #include "m68k_core.h" | 12 #include "m68k_core.h" |
13 #include "z80_to_x86.h" | 13 #include "z80_to_x86.h" |
14 #include "mem.h" | 14 #include "mem.h" |
15 #include "vdp.h" | 15 #include "vdp.h" |
16 #include "render.h" | 16 #include "render.h" |
17 #include "blastem.h" | 17 #include "genesis.h" |
18 #include "gdb_remote.h" | 18 #include "gdb_remote.h" |
19 #include "gst.h" | 19 #include "gst.h" |
20 #include "util.h" | 20 #include "util.h" |
21 #include "romdb.h" | 21 #include "romdb.h" |
22 #include "terminal.h" | 22 #include "terminal.h" |
23 #include "arena.h" | 23 #include "arena.h" |
24 #include "config.h" | |
24 | 25 |
25 #define BLASTEM_VERSION "0.4.1" | 26 #define BLASTEM_VERSION "0.4.1" |
26 | |
27 #define MCLKS_NTSC 53693175 | |
28 #define MCLKS_PAL 53203395 | |
29 | |
30 uint32_t MCLKS_PER_68K; | |
31 #define MCLKS_PER_YM 7 | |
32 #define MCLKS_PER_Z80 15 | |
33 #define MCLKS_PER_PSG (MCLKS_PER_Z80*16) | |
34 #define DEFAULT_SYNC_INTERVAL MCLKS_LINE | |
35 #define DEFAULT_LOWPASS_CUTOFF 3390 | |
36 | |
37 //TODO: Figure out the exact value for this | |
38 #define LINES_NTSC 262 | |
39 #define LINES_PAL 312 | |
40 | |
41 #define MAX_SOUND_CYCLES 100000 | |
42 | 27 |
43 #ifdef __ANDROID__ | 28 #ifdef __ANDROID__ |
44 #define FULLSCREEN_DEFAULT 1 | 29 #define FULLSCREEN_DEFAULT 1 |
45 #else | 30 #else |
46 #define FULLSCREEN_DEFAULT 0 | 31 #define FULLSCREEN_DEFAULT 0 |
47 #endif | 32 #endif |
48 | 33 |
49 uint16_t *cart; | |
50 uint16_t *ram; | |
51 uint8_t z80_ram[Z80_RAM_BYTES]; | |
52 | |
53 int headless = 0; | 34 int headless = 0; |
54 int exit_after = 0; | 35 int exit_after = 0; |
55 int z80_enabled = 1; | 36 int z80_enabled = 1; |
56 int frame_limit = 0; | 37 int frame_limit = 0; |
57 | 38 |
65 #define SMD_MAGIC1 0x03 | 46 #define SMD_MAGIC1 0x03 |
66 #define SMD_MAGIC2 0xAA | 47 #define SMD_MAGIC2 0xAA |
67 #define SMD_MAGIC3 0xBB | 48 #define SMD_MAGIC3 0xBB |
68 #define SMD_BLOCK_SIZE 0x4000 | 49 #define SMD_BLOCK_SIZE 0x4000 |
69 | 50 |
70 int load_smd_rom(long filesize, FILE * f) | 51 int load_smd_rom(long filesize, FILE * f, uint16_t **buffer) |
71 { | 52 { |
72 uint8_t block[SMD_BLOCK_SIZE]; | 53 uint8_t block[SMD_BLOCK_SIZE]; |
73 filesize -= SMD_HEADER_SIZE; | 54 filesize -= SMD_HEADER_SIZE; |
74 fseek(f, SMD_HEADER_SIZE, SEEK_SET); | 55 fseek(f, SMD_HEADER_SIZE, SEEK_SET); |
75 | 56 |
76 uint16_t * dst = cart = malloc(nearest_pow2(filesize)); | 57 uint16_t *dst = *buffer = malloc(nearest_pow2(filesize)); |
77 int rom_size = filesize; | 58 int rom_size = filesize; |
78 while (filesize > 0) { | 59 while (filesize > 0) { |
79 fread(block, 1, SMD_BLOCK_SIZE, f); | 60 fread(block, 1, SMD_BLOCK_SIZE, f); |
80 for (uint8_t *low = block, *high = (block+SMD_BLOCK_SIZE/2), *end = block+SMD_BLOCK_SIZE; high < end; high++, low++) { | 61 for (uint8_t *low = block, *high = (block+SMD_BLOCK_SIZE/2), *end = block+SMD_BLOCK_SIZE; high < end; high++, low++) { |
81 *(dst++) = *low << 8 | *high; | 62 *(dst++) = *low << 8 | *high; |
83 filesize -= SMD_BLOCK_SIZE; | 64 filesize -= SMD_BLOCK_SIZE; |
84 } | 65 } |
85 return rom_size; | 66 return rom_size; |
86 } | 67 } |
87 | 68 |
88 void byteswap_rom(int filesize) | 69 int load_rom(char * filename, uint16_t **dst) |
89 { | |
90 for(unsigned short * cur = cart; cur - cart < filesize/2; ++cur) | |
91 { | |
92 *cur = (*cur >> 8) | (*cur << 8); | |
93 } | |
94 } | |
95 | |
96 int load_rom(char * filename) | |
97 { | 70 { |
98 uint8_t header[10]; | 71 uint8_t header[10]; |
99 FILE * f = fopen(filename, "rb"); | 72 FILE * f = fopen(filename, "rb"); |
100 if (!f) { | 73 if (!f) { |
101 return 0; | 74 return 0; |
115 } | 88 } |
116 if (i == 8) { | 89 if (i == 8) { |
117 if (header[2]) { | 90 if (header[2]) { |
118 fatal_error("%s is a split SMD ROM which is not currently supported", filename); | 91 fatal_error("%s is a split SMD ROM which is not currently supported", filename); |
119 } | 92 } |
120 return load_smd_rom(filesize, f); | 93 return load_smd_rom(filesize, f, dst); |
121 } | 94 } |
122 } | 95 } |
123 cart = malloc(nearest_pow2(filesize)); | 96 *dst = malloc(nearest_pow2(filesize)); |
124 if (filesize != fread(cart, 1, filesize, f)) { | 97 if (filesize != fread(*dst, 1, filesize, f)) { |
125 fatal_error("Error reading from %s\n", filename); | 98 fatal_error("Error reading from %s\n", filename); |
126 } | 99 } |
127 fclose(f); | 100 fclose(f); |
128 return filesize; | 101 return filesize; |
129 } | 102 } |
130 | 103 |
131 uint16_t read_dma_value(uint32_t address) | 104 |
132 { | |
133 //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2 | |
134 uint16_t *ptr = get_native_pointer(address*2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen); | |
135 if (ptr) { | |
136 return *ptr; | |
137 } | |
138 //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area | |
139 return 0; | |
140 } | |
141 | |
142 uint16_t get_open_bus_value() | |
143 { | |
144 return read_dma_value(genesis->m68k->last_prefetch_address/2); | |
145 } | |
146 | |
147 void adjust_int_cycle(m68k_context * context, vdp_context * v_context) | |
148 { | |
149 //static int old_int_cycle = CYCLE_NEVER; | |
150 genesis_context *gen = context->system; | |
151 if (context->sync_cycle - context->current_cycle > gen->max_cycles) { | |
152 context->sync_cycle = context->current_cycle + gen->max_cycles; | |
153 } | |
154 context->int_cycle = CYCLE_NEVER; | |
155 if ((context->status & 0x7) < 6) { | |
156 uint32_t next_vint = vdp_next_vint(v_context); | |
157 if (next_vint != CYCLE_NEVER) { | |
158 context->int_cycle = next_vint; | |
159 context->int_num = 6; | |
160 } | |
161 if ((context->status & 0x7) < 4) { | |
162 uint32_t next_hint = vdp_next_hint(v_context); | |
163 if (next_hint != CYCLE_NEVER) { | |
164 next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; | |
165 if (next_hint < context->int_cycle) { | |
166 context->int_cycle = next_hint; | |
167 context->int_num = 4; | |
168 | |
169 } | |
170 } | |
171 } | |
172 } | |
173 if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { | |
174 context->int_pending = INT_PENDING_NONE; | |
175 } | |
176 /*if (context->int_cycle != old_int_cycle) { | |
177 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); | |
178 old_int_cycle = context->int_cycle; | |
179 }*/ | |
180 | |
181 context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; | |
182 if (context->should_return) { | |
183 context->target_cycle = context->current_cycle; | |
184 } else if (context->target_cycle < context->current_cycle) { | |
185 //Changes to SR can result in an interrupt cycle that's in the past | |
186 //This can cause issues with the implementation of STOP though | |
187 context->target_cycle = context->current_cycle; | |
188 } | |
189 /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", | |
190 context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), | |
191 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);*/ | |
192 } | |
193 | 105 |
194 int break_on_sync = 0; | 106 int break_on_sync = 0; |
195 char *save_state_path; | 107 char *save_state_path; |
196 | 108 |
197 //#define DO_DEBUG_PRINT | 109 |
198 #ifdef DO_DEBUG_PRINT | 110 |
199 #define dprintf printf | 111 |
200 #define dputs puts | |
201 #else | |
202 #define dprintf | |
203 #define dputs | |
204 #endif | |
205 | |
206 #define Z80_VINT_DURATION 128 | |
207 | |
208 void z80_next_int_pulse(z80_context * z_context) | |
209 { | |
210 genesis_context * gen = z_context->system; | |
211 z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp); | |
212 z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80; | |
213 } | |
214 | |
215 void sync_z80(z80_context * z_context, uint32_t mclks) | |
216 { | |
217 #ifndef NO_Z80 | |
218 if (z80_enabled) { | |
219 z80_run(z_context, mclks); | |
220 } else | |
221 #endif | |
222 { | |
223 z_context->current_cycle = mclks; | |
224 } | |
225 } | |
226 | |
227 void sync_sound(genesis_context * gen, uint32_t target) | |
228 { | |
229 //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); | |
230 while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { | |
231 uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; | |
232 //printf("Running PSG to cycle %d\n", cur_target); | |
233 psg_run(gen->psg, cur_target); | |
234 //printf("Running YM-2612 to cycle %d\n", cur_target); | |
235 ym_run(gen->ym, cur_target); | |
236 } | |
237 psg_run(gen->psg, target); | |
238 ym_run(gen->ym, target); | |
239 | |
240 //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); | |
241 } | |
242 | |
243 uint32_t last_frame_num; | |
244 | |
245 //My refresh emulation isn't currently good enough and causes more problems than it solves | |
246 #ifdef REFRESH_EMULATION | |
247 #define REFRESH_INTERVAL 128 | |
248 #define REFRESH_DELAY 2 | |
249 uint32_t last_sync_cycle; | |
250 uint32_t refresh_counter; | |
251 #endif | |
252 | |
253 m68k_context * sync_components(m68k_context * context, uint32_t address) | |
254 { | |
255 genesis_context * gen = context->system; | |
256 vdp_context * v_context = gen->vdp; | |
257 z80_context * z_context = gen->z80; | |
258 #ifdef REFRESH_EMULATION | |
259 //lame estimation of refresh cycle delay | |
260 if (!gen->bus_busy) { | |
261 refresh_counter += context->current_cycle - last_sync_cycle; | |
262 context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); | |
263 refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); | |
264 } | |
265 #endif | |
266 | |
267 uint32_t mclks = context->current_cycle; | |
268 sync_z80(z_context, mclks); | |
269 sync_sound(gen, mclks); | |
270 vdp_run_context(v_context, mclks); | |
271 if (v_context->frame != last_frame_num) { | |
272 //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); | |
273 last_frame_num = v_context->frame; | |
274 | |
275 if(exit_after){ | |
276 --exit_after; | |
277 if (!exit_after) { | |
278 exit(0); | |
279 } | |
280 } | |
281 | |
282 vdp_adjust_cycles(v_context, mclks); | |
283 io_adjust_cycles(gen->ports, context->current_cycle, mclks); | |
284 io_adjust_cycles(gen->ports+1, context->current_cycle, mclks); | |
285 io_adjust_cycles(gen->ports+2, context->current_cycle, mclks); | |
286 context->current_cycle -= mclks; | |
287 z80_adjust_cycles(z_context, mclks); | |
288 gen->ym->current_cycle -= mclks; | |
289 gen->psg->cycles -= mclks; | |
290 if (gen->ym->write_cycle != CYCLE_NEVER) { | |
291 gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0; | |
292 } | |
293 } | |
294 gen->frame_end = vdp_cycles_to_frame_end(v_context); | |
295 context->sync_cycle = gen->frame_end; | |
296 //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); | |
297 if (context->int_ack) { | |
298 //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); | |
299 vdp_int_ack(v_context); | |
300 context->int_ack = 0; | |
301 } | |
302 if (!address && (break_on_sync || gen->save_state)) { | |
303 context->sync_cycle = context->current_cycle + 1; | |
304 } | |
305 adjust_int_cycle(context, v_context); | |
306 if (address) { | |
307 if (break_on_sync) { | |
308 break_on_sync = 0; | |
309 debugger(context, address); | |
310 } | |
311 if (gen->save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) { | |
312 uint8_t slot = gen->save_state - 1; | |
313 gen->save_state = 0; | |
314 //advance Z80 core to the start of an instruction | |
315 while (!z_context->pc) | |
316 { | |
317 sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80); | |
318 } | |
319 char *save_path; | |
320 if (slot == QUICK_SAVE_SLOT) { | |
321 save_path = save_state_path; | |
322 } else { | |
323 char slotname[] = "slot_0.gst"; | |
324 slotname[5] = '0' + slot; | |
325 char const *parts[] = {gen->save_dir, PATH_SEP, slotname}; | |
326 save_path = alloc_concat_m(3, parts); | |
327 } | |
328 save_gst(gen, save_path, address); | |
329 printf("Saved state to %s\n", save_path); | |
330 if (slot != QUICK_SAVE_SLOT) { | |
331 free(save_path); | |
332 } | |
333 } else if(gen->save_state) { | |
334 context->sync_cycle = context->current_cycle + 1; | |
335 } | |
336 } | |
337 #ifdef REFRESH_EMULATION | |
338 last_sync_cycle = context->current_cycle; | |
339 #endif | |
340 return context; | |
341 } | |
342 | |
343 m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) | |
344 { | |
345 if (vdp_port & 0x2700E0) { | |
346 fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port); | |
347 } | |
348 vdp_port &= 0x1F; | |
349 //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); | |
350 sync_components(context, 0); | |
351 genesis_context * gen = context->system; | |
352 vdp_context *v_context = gen->vdp; | |
353 if (vdp_port < 0x10) { | |
354 int blocked; | |
355 uint32_t before_cycle = v_context->cycles; | |
356 if (vdp_port < 4) { | |
357 | |
358 while (vdp_data_port_write(v_context, value) < 0) { | |
359 while(v_context->flags & FLAG_DMA_RUN) { | |
360 vdp_run_dma_done(v_context, gen->frame_end); | |
361 if (v_context->cycles >= gen->frame_end) { | |
362 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
363 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
364 if (m68k_cycle_diff < cycle_diff) { | |
365 m68k_cycle_diff += MCLKS_PER_68K; | |
366 } | |
367 context->current_cycle += m68k_cycle_diff; | |
368 gen->bus_busy = 1; | |
369 sync_components(context, 0); | |
370 gen->bus_busy = 0; | |
371 } | |
372 } | |
373 //context->current_cycle = v_context->cycles; | |
374 } | |
375 } else if(vdp_port < 8) { | |
376 blocked = vdp_control_port_write(v_context, value); | |
377 if (blocked) { | |
378 while (blocked) { | |
379 while(v_context->flags & FLAG_DMA_RUN) { | |
380 vdp_run_dma_done(v_context, gen->frame_end); | |
381 if (v_context->cycles >= gen->frame_end) { | |
382 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
383 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
384 if (m68k_cycle_diff < cycle_diff) { | |
385 m68k_cycle_diff += MCLKS_PER_68K; | |
386 } | |
387 context->current_cycle += m68k_cycle_diff; | |
388 gen->bus_busy = 1; | |
389 sync_components(context, 0); | |
390 gen->bus_busy = 0; | |
391 } | |
392 if (!(v_context->flags & FLAG_DMA_RUN)) { | |
393 //two more slots of delay are needed to kill sufficient sprite capacity in Overdrive | |
394 //TODO: Measure exact value with logic analyzer | |
395 vdp_run_context(v_context, v_context->cycles + 1); | |
396 vdp_run_context(v_context, v_context->cycles + 1); | |
397 } | |
398 } | |
399 | |
400 if (blocked < 0) { | |
401 blocked = vdp_control_port_write(v_context, value); | |
402 } else { | |
403 blocked = 0; | |
404 } | |
405 } | |
406 } else { | |
407 context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context); | |
408 //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); | |
409 adjust_int_cycle(context, v_context); | |
410 } | |
411 } else { | |
412 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
413 } | |
414 if (v_context->cycles != before_cycle) { | |
415 //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); | |
416 uint32_t cycle_diff = v_context->cycles - context->current_cycle; | |
417 uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; | |
418 if (m68k_cycle_diff < cycle_diff) { | |
419 m68k_cycle_diff += MCLKS_PER_68K; | |
420 } | |
421 context->current_cycle += m68k_cycle_diff; | |
422 #ifdef REFRESH_EMULATION | |
423 last_sync_cycle = context->current_cycle; | |
424 #endif | |
425 //Lock the Z80 out of the bus until the VDP access is complete | |
426 gen->bus_busy = 1; | |
427 sync_z80(gen->z80, v_context->cycles); | |
428 gen->bus_busy = 0; | |
429 } | |
430 } else if (vdp_port < 0x18) { | |
431 psg_write(gen->psg, value); | |
432 } else { | |
433 //TODO: Implement undocumented test register(s) | |
434 } | |
435 return context; | |
436 } | |
437 | |
438 m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value) | |
439 { | |
440 return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0)); | |
441 } | |
442 | |
443 void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value) | |
444 { | |
445 z80_context * context = vcontext; | |
446 genesis_context * gen = context->system; | |
447 vdp_port &= 0xFF; | |
448 if (vdp_port & 0xE0) { | |
449 fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port); | |
450 } | |
451 if (vdp_port < 0x10) { | |
452 //These probably won't currently interact well with the 68K accessing the VDP | |
453 vdp_run_context(gen->vdp, context->current_cycle); | |
454 if (vdp_port < 4) { | |
455 vdp_data_port_write(gen->vdp, value << 8 | value); | |
456 } else if (vdp_port < 8) { | |
457 vdp_control_port_write(gen->vdp, value << 8 | value); | |
458 } else { | |
459 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
460 } | |
461 } else if (vdp_port < 0x18) { | |
462 sync_sound(gen, context->current_cycle); | |
463 psg_write(gen->psg, value); | |
464 } else { | |
465 vdp_test_port_write(gen->vdp, value); | |
466 } | |
467 return context; | |
468 } | |
469 | |
470 uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) | |
471 { | |
472 if (vdp_port & 0x2700E0) { | |
473 fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port); | |
474 } | |
475 vdp_port &= 0x1F; | |
476 uint16_t value; | |
477 sync_components(context, 0); | |
478 genesis_context *gen = context->system; | |
479 vdp_context * v_context = gen->vdp; | |
480 uint32_t before_cycle = v_context->cycles; | |
481 if (vdp_port < 0x10) { | |
482 if (vdp_port < 4) { | |
483 value = vdp_data_port_read(v_context); | |
484 } else if(vdp_port < 8) { | |
485 value = vdp_control_port_read(v_context); | |
486 } else { | |
487 value = vdp_hv_counter_read(v_context); | |
488 //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); | |
489 } | |
490 } else if (vdp_port < 0x18){ | |
491 fatal_error("Illegal read from PSG port %X\n", vdp_port); | |
492 } else { | |
493 value = vdp_test_port_read(v_context); | |
494 } | |
495 if (v_context->cycles != before_cycle) { | |
496 //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); | |
497 context->current_cycle = v_context->cycles; | |
498 #ifdef REFRES_EMULATION | |
499 last_sync_cycle = context->current_cycle; | |
500 #endif | |
501 //Lock the Z80 out of the bus until the VDP access is complete | |
502 genesis_context *gen = context->system; | |
503 gen->bus_busy = 1; | |
504 sync_z80(gen->z80, v_context->cycles); | |
505 gen->bus_busy = 0; | |
506 } | |
507 return value; | |
508 } | |
509 | |
510 uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context) | |
511 { | |
512 uint16_t value = vdp_port_read(vdp_port, context); | |
513 if (vdp_port & 1) { | |
514 return value; | |
515 } else { | |
516 return value >> 8; | |
517 } | |
518 } | |
519 | |
520 uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext) | |
521 { | |
522 z80_context * context = vcontext; | |
523 if (vdp_port & 0xE0) { | |
524 fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port); | |
525 } | |
526 genesis_context * gen = context->system; | |
527 //VDP access goes over the 68K bus like a bank area access | |
528 //typical delay from bus arbitration | |
529 context->current_cycle += 3 * MCLKS_PER_Z80; | |
530 //TODO: add cycle for an access right after a previous one | |
531 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
532 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
533 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
534 | |
535 | |
536 vdp_port &= 0x1F; | |
537 uint16_t ret; | |
538 if (vdp_port < 0x10) { | |
539 //These probably won't currently interact well with the 68K accessing the VDP | |
540 vdp_run_context(gen->vdp, context->current_cycle); | |
541 if (vdp_port < 4) { | |
542 ret = vdp_data_port_read(gen->vdp); | |
543 } else if (vdp_port < 8) { | |
544 ret = vdp_control_port_read(gen->vdp); | |
545 } else { | |
546 fatal_error("Illegal write to HV Counter port %X\n", vdp_port); | |
547 } | |
548 } else { | |
549 //TODO: Figure out the correct value today | |
550 ret = 0xFFFF; | |
551 } | |
552 return vdp_port & 1 ? ret : ret >> 8; | |
553 } | |
554 | |
555 uint32_t zram_counter = 0; | |
556 | |
557 m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) | |
558 { | |
559 genesis_context * gen = context->system; | |
560 if (location < 0x10000) { | |
561 //Access to Z80 memory incurs a one 68K cycle wait state | |
562 context->current_cycle += MCLKS_PER_68K; | |
563 if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { | |
564 location &= 0x7FFF; | |
565 if (location < 0x4000) { | |
566 z80_ram[location & 0x1FFF] = value; | |
567 #ifndef NO_Z80 | |
568 z80_handle_code_write(location & 0x1FFF, gen->z80); | |
569 #endif | |
570 } else if (location < 0x6000) { | |
571 sync_sound(gen, context->current_cycle); | |
572 if (location & 1) { | |
573 ym_data_write(gen->ym, value); | |
574 } else if(location & 2) { | |
575 ym_address_write_part2(gen->ym, value); | |
576 } else { | |
577 ym_address_write_part1(gen->ym, value); | |
578 } | |
579 } else if (location == 0x6000) { | |
580 gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF; | |
581 if (gen->z80->bank_reg < 0x80) { | |
582 gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]); | |
583 } else { | |
584 gen->z80->mem_pointers[1] = NULL; | |
585 } | |
586 } else { | |
587 fatal_error("68K write to unhandled Z80 address %X\n", location); | |
588 } | |
589 } | |
590 } else { | |
591 location &= 0x1FFF; | |
592 if (location < 0x100) { | |
593 switch(location/2) | |
594 { | |
595 case 0x1: | |
596 io_data_write(gen->ports, value, context->current_cycle); | |
597 break; | |
598 case 0x2: | |
599 io_data_write(gen->ports+1, value, context->current_cycle); | |
600 break; | |
601 case 0x3: | |
602 io_data_write(gen->ports+2, value, context->current_cycle); | |
603 break; | |
604 case 0x4: | |
605 gen->ports[0].control = value; | |
606 break; | |
607 case 0x5: | |
608 gen->ports[1].control = value; | |
609 break; | |
610 case 0x6: | |
611 gen->ports[2].control = value; | |
612 break; | |
613 } | |
614 } else { | |
615 if (location == 0x1100) { | |
616 if (value & 1) { | |
617 dputs("bus requesting Z80"); | |
618 if (z80_enabled) { | |
619 z80_assert_busreq(gen->z80, context->current_cycle); | |
620 } else { | |
621 gen->z80->busack = 1; | |
622 } | |
623 } else { | |
624 if (gen->z80->busreq) { | |
625 dputs("releasing z80 bus"); | |
626 #ifdef DO_DEBUG_PRINT | |
627 char fname[20]; | |
628 sprintf(fname, "zram-%d", zram_counter++); | |
629 FILE * f = fopen(fname, "wb"); | |
630 fwrite(z80_ram, 1, sizeof(z80_ram), f); | |
631 fclose(f); | |
632 #endif | |
633 } | |
634 if (z80_enabled) { | |
635 z80_clear_busreq(gen->z80, context->current_cycle); | |
636 } else { | |
637 gen->z80->busack = 0; | |
638 } | |
639 } | |
640 } else if (location == 0x1200) { | |
641 sync_z80(gen->z80, context->current_cycle); | |
642 if (value & 1) { | |
643 if (z80_enabled) { | |
644 z80_clear_reset(gen->z80, context->current_cycle); | |
645 } else { | |
646 gen->z80->reset = 0; | |
647 } | |
648 } else { | |
649 if (z80_enabled) { | |
650 z80_assert_reset(gen->z80, context->current_cycle); | |
651 } else { | |
652 gen->z80->reset = 1; | |
653 } | |
654 } | |
655 } | |
656 } | |
657 } | |
658 return context; | |
659 } | |
660 | |
661 m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) | |
662 { | |
663 if (location < 0x10000 || (location & 0x1FFF) >= 0x100) { | |
664 return io_write(location, context, value >> 8); | |
665 } else { | |
666 return io_write(location, context, value); | |
667 } | |
668 } | |
669 | |
670 #define USA 0x80 | |
671 #define JAP 0x00 | |
672 #define EUR 0xC0 | |
673 #define NO_DISK 0x20 | |
674 uint8_t version_reg = NO_DISK | USA; | |
675 | |
676 uint8_t io_read(uint32_t location, m68k_context * context) | |
677 { | |
678 uint8_t value; | |
679 genesis_context *gen = context->system; | |
680 if (location < 0x10000) { | |
681 //Access to Z80 memory incurs a one 68K cycle wait state | |
682 context->current_cycle += MCLKS_PER_68K; | |
683 if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { | |
684 location &= 0x7FFF; | |
685 if (location < 0x4000) { | |
686 value = z80_ram[location & 0x1FFF]; | |
687 } else if (location < 0x6000) { | |
688 sync_sound(gen, context->current_cycle); | |
689 value = ym_read_status(gen->ym); | |
690 } else { | |
691 value = 0xFF; | |
692 } | |
693 } else { | |
694 value = 0xFF; | |
695 } | |
696 } else { | |
697 location &= 0x1FFF; | |
698 if (location < 0x100) { | |
699 switch(location/2) | |
700 { | |
701 case 0x0: | |
702 //version bits should be 0 for now since we're not emulating TMSS | |
703 value = version_reg; | |
704 break; | |
705 case 0x1: | |
706 value = io_data_read(gen->ports, context->current_cycle); | |
707 break; | |
708 case 0x2: | |
709 value = io_data_read(gen->ports+1, context->current_cycle); | |
710 break; | |
711 case 0x3: | |
712 value = io_data_read(gen->ports+2, context->current_cycle); | |
713 break; | |
714 case 0x4: | |
715 value = gen->ports[0].control; | |
716 break; | |
717 case 0x5: | |
718 value = gen->ports[1].control; | |
719 break; | |
720 case 0x6: | |
721 value = gen->ports[2].control; | |
722 break; | |
723 default: | |
724 value = 0xFF; | |
725 } | |
726 } else { | |
727 if (location == 0x1100) { | |
728 value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack; | |
729 value |= (get_open_bus_value() >> 8) & 0xFE; | |
730 dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset); | |
731 } else if (location == 0x1200) { | |
732 value = !gen->z80->reset; | |
733 } else { | |
734 value = 0xFF; | |
735 printf("Byte read of unknown IO location: %X\n", location); | |
736 } | |
737 } | |
738 } | |
739 return value; | |
740 } | |
741 | |
742 uint16_t io_read_w(uint32_t location, m68k_context * context) | |
743 { | |
744 uint16_t value = io_read(location, context); | |
745 if (location < 0x10000 || (location & 0x1FFF) < 0x100) { | |
746 value = value | (value << 8); | |
747 } else { | |
748 value <<= 8; | |
749 value |= get_open_bus_value() & 0xFF; | |
750 } | |
751 return value; | |
752 } | |
753 | |
754 void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value) | |
755 { | |
756 z80_context * context = vcontext; | |
757 genesis_context * gen = context->system; | |
758 sync_sound(gen, context->current_cycle); | |
759 if (location & 1) { | |
760 ym_data_write(gen->ym, value); | |
761 } else if (location & 2) { | |
762 ym_address_write_part2(gen->ym, value); | |
763 } else { | |
764 ym_address_write_part1(gen->ym, value); | |
765 } | |
766 return context; | |
767 } | |
768 | |
769 uint8_t z80_read_ym(uint32_t location, void * vcontext) | |
770 { | |
771 z80_context * context = vcontext; | |
772 genesis_context * gen = context->system; | |
773 sync_sound(gen, context->current_cycle); | |
774 return ym_read_status(gen->ym); | |
775 } | |
776 | |
777 uint8_t z80_read_bank(uint32_t location, void * vcontext) | |
778 { | |
779 z80_context * context = vcontext; | |
780 genesis_context *gen = context->system; | |
781 if (gen->bus_busy) { | |
782 context->current_cycle = context->sync_cycle; | |
783 } | |
784 //typical delay from bus arbitration | |
785 context->current_cycle += 3 * MCLKS_PER_Z80; | |
786 //TODO: add cycle for an access right after a previous one | |
787 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
788 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
789 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
790 | |
791 location &= 0x7FFF; | |
792 if (context->mem_pointers[1]) { | |
793 return context->mem_pointers[1][location ^ 1]; | |
794 } | |
795 uint32_t address = context->bank_reg << 15 | location; | |
796 if (address >= 0xC00000 && address < 0xE00000) { | |
797 return z80_vdp_port_read(location & 0xFF, context); | |
798 } else { | |
799 fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15); | |
800 } | |
801 return 0; | |
802 } | |
803 | |
804 void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value) | |
805 { | |
806 z80_context * context = vcontext; | |
807 genesis_context *gen = context->system; | |
808 if (gen->bus_busy) { | |
809 context->current_cycle = context->sync_cycle; | |
810 } | |
811 //typical delay from bus arbitration | |
812 context->current_cycle += 3 * MCLKS_PER_Z80; | |
813 //TODO: add cycle for an access right after a previous one | |
814 //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high | |
815 // Needs a new logic analyzer capture to get the actual delay on the 68K side | |
816 gen->m68k->current_cycle += 8 * MCLKS_PER_68K; | |
817 | |
818 location &= 0x7FFF; | |
819 uint32_t address = context->bank_reg << 15 | location; | |
820 if (address >= 0xE00000) { | |
821 address &= 0xFFFF; | |
822 ((uint8_t *)ram)[address ^ 1] = value; | |
823 } else if (address >= 0xC00000) { | |
824 z80_vdp_port_write(location & 0xFF, context, value); | |
825 } else { | |
826 fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address); | |
827 } | |
828 return context; | |
829 } | |
830 | |
831 void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value) | |
832 { | |
833 z80_context * context = vcontext; | |
834 | |
835 context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF; | |
836 if (context->bank_reg < 0x100) { | |
837 genesis_context *gen = context->system; | |
838 context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen); | |
839 } else { | |
840 context->mem_pointers[1] = NULL; | |
841 } | |
842 | |
843 return context; | |
844 } | |
845 | |
846 void set_speed_percent(genesis_context * context, uint32_t percent) | |
847 { | |
848 uint32_t old_clock = context->master_clock; | |
849 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; | |
850 while (context->ym->current_cycle != context->psg->cycles) { | |
851 sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); | |
852 } | |
853 ym_adjust_master_clock(context->ym, context->master_clock); | |
854 psg_adjust_master_clock(context->psg, context->master_clock); | |
855 } | |
856 | 112 |
857 char * save_filename; | 113 char * save_filename; |
858 genesis_context *genesis; | 114 genesis_context *genesis; |
859 genesis_context *menu_context; | 115 genesis_context *menu_context; |
860 genesis_context *game_context; | 116 genesis_context *game_context; |
871 fwrite(game_context->save_storage, 1, game_context->save_size, f); | 127 fwrite(game_context->save_storage, 1, game_context->save_size, f); |
872 fclose(f); | 128 fclose(f); |
873 printf("Saved %s to %s\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); | 129 printf("Saved %s to %s\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); |
874 } | 130 } |
875 | 131 |
876 #ifndef NO_Z80 | 132 |
877 const memmap_chunk z80_map[] = { | |
878 { 0x0000, 0x4000, 0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, z80_ram, NULL, NULL, NULL, NULL }, | |
879 { 0x8000, 0x10000, 0x7FFF, 0, 0, 0, NULL, NULL, NULL, z80_read_bank, z80_write_bank}, | |
880 { 0x4000, 0x6000, 0x0003, 0, 0, 0, NULL, NULL, NULL, z80_read_ym, z80_write_ym}, | |
881 { 0x6000, 0x6100, 0xFFFF, 0, 0, 0, NULL, NULL, NULL, NULL, z80_write_bank_reg}, | |
882 { 0x7F00, 0x8000, 0x00FF, 0, 0, 0, NULL, NULL, NULL, z80_vdp_port_read, z80_vdp_port_write} | |
883 }; | |
884 #endif | |
885 | |
886 genesis_context *alloc_init_genesis(rom_info *rom, int fps, uint32_t ym_opts) | |
887 { | |
888 genesis_context *gen = calloc(1, sizeof(genesis_context)); | |
889 gen->master_clock = gen->normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL; | |
890 | |
891 gen->vdp = malloc(sizeof(vdp_context)); | |
892 init_vdp_context(gen->vdp, version_reg & 0x40); | |
893 gen->frame_end = vdp_cycles_to_frame_end(gen->vdp); | |
894 char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval; | |
895 gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL; | |
896 | |
897 char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval; | |
898 uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF; | |
899 | |
900 gen->ym = malloc(sizeof(ym2612_context)); | |
901 ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff); | |
902 | |
903 gen->psg = malloc(sizeof(psg_context)); | |
904 psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff); | |
905 | |
906 gen->z80 = calloc(1, sizeof(z80_context)); | |
907 #ifndef NO_Z80 | |
908 z80_options *z_opts = malloc(sizeof(z80_options)); | |
909 init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80); | |
910 init_z80_context(gen->z80, z_opts); | |
911 z80_assert_reset(gen->z80, 0); | |
912 #endif | |
913 | |
914 gen->z80->system = gen; | |
915 gen->z80->mem_pointers[0] = z80_ram; | |
916 gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart; | |
917 | |
918 gen->cart = cart; | |
919 gen->work_ram = ram; | |
920 gen->zram = z80_ram; | |
921 setup_io_devices(config, rom, gen); | |
922 | |
923 gen->save_type = rom->save_type; | |
924 gen->save_type = rom->save_type; | |
925 if (gen->save_type != SAVE_NONE) { | |
926 gen->save_ram_mask = rom->save_mask; | |
927 gen->save_size = rom->save_size; | |
928 gen->save_storage = rom->save_buffer; | |
929 gen->eeprom_map = rom->eeprom_map; | |
930 gen->num_eeprom = rom->num_eeprom; | |
931 if (gen->save_type == SAVE_I2C) { | |
932 eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); | |
933 } | |
934 } else { | |
935 gen->save_storage = NULL; | |
936 } | |
937 | |
938 m68k_options *opts = malloc(sizeof(m68k_options)); | |
939 init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K); | |
940 //TODO: make this configurable | |
941 opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; | |
942 gen->m68k = init_68k_context(opts, NULL); | |
943 gen->m68k->system = gen; | |
944 | |
945 for (int i = 0; i < rom->map_chunks; i++) | |
946 { | |
947 if (rom->map[i].flags & MMAP_PTR_IDX) { | |
948 gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; | |
949 } | |
950 } | |
951 | |
952 return gen; | |
953 } | |
954 | |
955 void free_genesis(genesis_context *gen) | |
956 { | |
957 vdp_free(gen->vdp); | |
958 m68k_options_free(gen->m68k->options); | |
959 free(gen->m68k); | |
960 z80_options_free(gen->z80->options); | |
961 free(gen->z80); | |
962 ym_free(gen->ym); | |
963 psg_free(gen->psg); | |
964 free(gen->save_storage); | |
965 free(gen->save_dir); | |
966 free(gen->lock_on); | |
967 } | |
968 | |
969 void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger) | |
970 { | |
971 | |
972 if (statefile) { | |
973 uint32_t pc = load_gst(gen, statefile); | |
974 if (!pc) { | |
975 fatal_error("Failed to load save state %s\n", statefile); | |
976 } | |
977 printf("Loaded %s\n", statefile); | |
978 if (debugger) { | |
979 insert_breakpoint(gen->m68k, pc, debugger); | |
980 } | |
981 adjust_int_cycle(gen->m68k, gen->vdp); | |
982 start_68k_context(gen->m68k, pc); | |
983 } else { | |
984 if (debugger) { | |
985 uint32_t address = cart[2] << 16 | cart[3]; | |
986 insert_breakpoint(gen->m68k, address, debugger); | |
987 } | |
988 m68k_reset(gen->m68k); | |
989 } | |
990 } | |
991 | 133 |
992 char *title; | 134 char *title; |
993 | |
994 void update_title(char *rom_name) | 135 void update_title(char *rom_name) |
995 { | 136 { |
996 if (title) { | 137 if (title) { |
997 free(title); | 138 free(title); |
998 title = NULL; | 139 title = NULL; |
999 } | 140 } |
1000 title = alloc_concat(rom_name, " - BlastEm"); | 141 title = alloc_concat(rom_name, " - BlastEm"); |
1001 render_update_caption(title); | 142 render_update_caption(title); |
1002 } | |
1003 | |
1004 void set_region(rom_info *info, uint8_t region) | |
1005 { | |
1006 if (!region) { | |
1007 char * def_region = tern_find_ptr(config, "default_region"); | |
1008 if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) { | |
1009 region = translate_region_char(toupper(*def_region)); | |
1010 } else { | |
1011 region = info->regions; | |
1012 } | |
1013 } | |
1014 if (region & REGION_E) { | |
1015 version_reg = NO_DISK | EUR; | |
1016 } else if (region & REGION_J) { | |
1017 version_reg = NO_DISK | JAP; | |
1018 } else { | |
1019 version_reg = NO_DISK | USA; | |
1020 } | |
1021 } | 143 } |
1022 | 144 |
1023 void setup_saves(char *fname, rom_info *info, genesis_context *context) | 145 void setup_saves(char *fname, rom_info *info, genesis_context *context) |
1024 { | 146 { |
1025 char * barename = basename_no_extension(fname); | 147 char * barename = basename_no_extension(fname); |
1057 int width = -1; | 179 int width = -1; |
1058 int height = -1; | 180 int height = -1; |
1059 int debug = 0; | 181 int debug = 0; |
1060 int ym_log = 0; | 182 int ym_log = 0; |
1061 int loaded = 0; | 183 int loaded = 0; |
1062 uint8_t force_version = 0; | 184 uint8_t force_region = 0; |
1063 char * romfname = NULL; | 185 char * romfname = NULL; |
1064 FILE *address_log = NULL; | 186 FILE *address_log = NULL; |
1065 char * statefile = NULL; | 187 char * statefile = NULL; |
1066 int rom_size, lock_on_size; | 188 int rom_size, lock_on_size; |
1067 uint16_t *lock_on = NULL; | 189 uint16_t *cart = NULL, *lock_on = NULL; |
1068 uint8_t * debuggerfun = NULL; | 190 uint8_t * debuggerfun = NULL; |
1069 uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1; | 191 uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1; |
1070 uint8_t debug_target = 0; | 192 uint8_t debug_target = 0; |
1071 for (int i = 1; i < argc; i++) { | 193 for (int i = 1; i < argc; i++) { |
1072 if (argv[i][0] == '-') { | 194 if (argv[i][0] == '-') { |
1109 case 'r': | 231 case 'r': |
1110 i++; | 232 i++; |
1111 if (i >= argc) { | 233 if (i >= argc) { |
1112 fatal_error("-r must be followed by region (J, U or E)\n"); | 234 fatal_error("-r must be followed by region (J, U or E)\n"); |
1113 } | 235 } |
1114 force_version = translate_region_char(toupper(argv[i][0])); | 236 force_region = translate_region_char(toupper(argv[i][0])); |
1115 if (!force_version) { | 237 if (!force_region) { |
1116 fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]); | 238 fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]); |
1117 } | 239 } |
1118 break; | 240 break; |
1119 case 's': | 241 case 's': |
1120 i++; | 242 i++; |
1132 case 'o': { | 254 case 'o': { |
1133 i++; | 255 i++; |
1134 if (i >= argc) { | 256 if (i >= argc) { |
1135 fatal_error("-o must be followed by a lock on cartridge filename\n"); | 257 fatal_error("-o must be followed by a lock on cartridge filename\n"); |
1136 } | 258 } |
1137 uint16_t *tmp = cart; | 259 lock_on_size = load_rom(argv[i], &lock_on); |
1138 lock_on_size = load_rom(argv[i]); | 260 if (!lock_on_size) { |
1139 if (lock_on_size) { | |
1140 byteswap_rom(lock_on_size); | |
1141 lock_on = cart; | |
1142 } else { | |
1143 fatal_error("Failed to load lock on cartridge %s\n", argv[i]); | 261 fatal_error("Failed to load lock on cartridge %s\n", argv[i]); |
1144 } | 262 } |
1145 cart = tmp; | |
1146 break; | 263 break; |
1147 } | 264 } |
1148 case 'h': | 265 case 'h': |
1149 info_message( | 266 info_message( |
1150 "Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n" | 267 "Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n" |
1164 return 0; | 281 return 0; |
1165 default: | 282 default: |
1166 fatal_error("Unrecognized switch %s\n", argv[i]); | 283 fatal_error("Unrecognized switch %s\n", argv[i]); |
1167 } | 284 } |
1168 } else if (!loaded) { | 285 } else if (!loaded) { |
1169 if (!(rom_size = load_rom(argv[i]))) { | 286 if (!(rom_size = load_rom(argv[i], &cart))) { |
1170 fatal_error("Failed to open %s for reading\n", argv[i]); | 287 fatal_error("Failed to open %s for reading\n", argv[i]); |
1171 } | 288 } |
1172 romfname = argv[i]; | 289 romfname = argv[i]; |
1173 loaded = 1; | 290 loaded = 1; |
1174 } else if (width < 0) { | 291 } else if (width < 0) { |
1183 romfname = tern_find_path(config, "ui\0rom\0").ptrval; | 300 romfname = tern_find_path(config, "ui\0rom\0").ptrval; |
1184 if (!romfname) { | 301 if (!romfname) { |
1185 romfname = "menu.bin"; | 302 romfname = "menu.bin"; |
1186 } | 303 } |
1187 if (is_absolute_path(romfname)) { | 304 if (is_absolute_path(romfname)) { |
1188 if (!(rom_size = load_rom(romfname))) { | 305 if (!(rom_size = load_rom(romfname, &cart))) { |
1189 fatal_error("Failed to open UI ROM %s for reading", romfname); | 306 fatal_error("Failed to open UI ROM %s for reading", romfname); |
1190 } | 307 } |
1191 } else { | 308 } else { |
1192 long fsize; | 309 long fsize; |
1193 cart = (uint16_t *)read_bundled_file(romfname, &fsize); | 310 cart = (uint16_t *)read_bundled_file(romfname, &fsize); |
1197 rom_size = nearest_pow2(fsize); | 314 rom_size = nearest_pow2(fsize); |
1198 if (rom_size > fsize) { | 315 if (rom_size > fsize) { |
1199 cart = realloc(cart, rom_size); | 316 cart = realloc(cart, rom_size); |
1200 } | 317 } |
1201 } | 318 } |
1202 //TODO: load relative to executable or from assets depending on platform | |
1203 | 319 |
1204 loaded = 1; | 320 loaded = 1; |
1205 } | 321 } |
1206 char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval; | 322 |
1207 if (!m68k_divider) { | |
1208 m68k_divider = "7"; | |
1209 } | |
1210 MCLKS_PER_68K = atoi(m68k_divider); | |
1211 if (!MCLKS_PER_68K) { | |
1212 MCLKS_PER_68K = 7; | |
1213 } | |
1214 ram = malloc(RAM_WORDS * sizeof(uint16_t)); | |
1215 memmap_chunk base_map[] = { | |
1216 {0xE00000, 0x1000000, 0xFFFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, | |
1217 NULL, NULL, NULL, NULL}, | |
1218 {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, 0, NULL, | |
1219 (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, | |
1220 (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, | |
1221 {0xA00000, 0xA12000, 0x1FFFF, 0, 0, 0, NULL, | |
1222 (read_16_fun)io_read_w, (write_16_fun)io_write_w, | |
1223 (read_8_fun)io_read, (write_8_fun)io_write} | |
1224 }; | |
1225 tern_node *rom_db = load_rom_db(); | |
1226 rom_info info = configure_rom(rom_db, cart, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0])); | |
1227 byteswap_rom(rom_size); | |
1228 set_region(&info, force_version); | |
1229 update_title(info.name); | |
1230 int def_width = 0; | 323 int def_width = 0; |
1231 char *config_width = tern_find_path(config, "video\0width\0").ptrval; | 324 char *config_width = tern_find_path(config, "video\0width\0").ptrval; |
1232 if (config_width) { | 325 if (config_width) { |
1233 def_width = atoi(config_width); | 326 def_width = atoi(config_width); |
1234 } | 327 } |
1235 if (!def_width) { | 328 if (!def_width) { |
1236 def_width = 640; | 329 def_width = 640; |
1237 } | 330 } |
1238 width = width < 320 ? def_width : width; | 331 width = width < 320 ? def_width : width; |
1239 height = height < 240 ? (width/320) * 240 : height; | 332 height = height < 240 ? (width/320) * 240 : height; |
1240 uint32_t fps = 60; | 333 |
1241 if (version_reg & 0x40) { | |
1242 fps = 50; | |
1243 } | |
1244 char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0").ptrval; | 334 char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0").ptrval; |
1245 if (config_fullscreen && !strcmp("on", config_fullscreen)) { | 335 if (config_fullscreen && !strcmp("on", config_fullscreen)) { |
1246 fullscreen = !fullscreen; | 336 fullscreen = !fullscreen; |
1247 } | 337 } |
1248 if (!headless) { | 338 if (!headless) { |
1249 render_init(width, height, title, fps, fullscreen); | 339 render_init(width, height, "BlastEm", fullscreen); |
1250 } | 340 } |
1251 | 341 |
1252 genesis = alloc_init_genesis(&info, fps, (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0); | 342 rom_info info; |
1253 genesis->lock_on = lock_on; | 343 uint32_t ym_opts = (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0; |
344 genesis = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts, force_region, &info); | |
1254 setup_saves(romfname, &info, genesis); | 345 setup_saves(romfname, &info, genesis); |
346 update_title(info.name); | |
1255 if (menu) { | 347 if (menu) { |
1256 menu_context = genesis; | 348 menu_context = genesis; |
1257 } else { | 349 } else { |
1258 genesis->m68k->options->address_log = address_log; | 350 genesis->m68k->options->address_log = address_log; |
1259 game_context = genesis; | 351 game_context = genesis; |
1271 if (game_context->save_type != SAVE_NONE) { | 363 if (game_context->save_type != SAVE_NONE) { |
1272 genesis = game_context; | 364 genesis = game_context; |
1273 persist_save(); | 365 persist_save(); |
1274 genesis = menu_context; | 366 genesis = menu_context; |
1275 } | 367 } |
1276 free(game_context->cart); | 368 //swap to game context arena and mark all allocated pages in it free |
1277 base_map[0].buffer = ram = game_context->work_ram; | |
1278 } else { | |
1279 base_map[0].buffer = ram = malloc(RAM_WORDS * sizeof(uint16_t)); | |
1280 } | |
1281 memset(ram, 0, RAM_WORDS * sizeof(uint16_t)); | |
1282 if (!(rom_size = load_rom(menu_context->next_rom))) { | |
1283 fatal_error("Failed to open %s for reading\n", menu_context->next_rom); | |
1284 } | |
1285 info = configure_rom(rom_db, cart, rom_size, NULL, 0, base_map, sizeof(base_map)/sizeof(base_map[0])); | |
1286 byteswap_rom(rom_size); | |
1287 set_region(&info, force_version); | |
1288 update_title(info.name); | |
1289 if (!game_context) { | |
1290 //start a new arena and save old one in suspended genesis context | |
1291 genesis->arena = start_new_arena(); | |
1292 } else { | |
1293 genesis->arena = set_current_arena(game_context->arena); | 369 genesis->arena = set_current_arena(game_context->arena); |
1294 mark_all_free(); | 370 mark_all_free(); |
1295 free_genesis(game_context); | 371 free_genesis(game_context); |
372 } else { | |
373 //start a new arena and save old one in suspended genesis context | |
374 genesis->arena = start_new_arena(); | |
375 } | |
376 if (!(rom_size = load_rom(menu_context->next_rom, &cart))) { | |
377 fatal_error("Failed to open %s for reading\n", menu_context->next_rom); | |
1296 } | 378 } |
1297 //allocate new genesis context | 379 //allocate new genesis context |
1298 game_context = alloc_init_genesis(&info, fps, ym_log ? YM_OPT_WAVE_LOG : 0); | 380 game_context = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts,force_region, &info); |
1299 menu_context->next_context = game_context; | 381 menu_context->next_context = game_context; |
1300 game_context->next_context = menu_context; | 382 game_context->next_context = menu_context; |
1301 setup_saves(menu_context->next_rom, &info, game_context); | 383 setup_saves(menu_context->next_rom, &info, game_context); |
384 update_title(info.name); | |
1302 free(menu_context->next_rom); | 385 free(menu_context->next_rom); |
1303 menu_context->next_rom = NULL; | 386 menu_context->next_rom = NULL; |
1304 menu = 0; | 387 menu = 0; |
1305 genesis = game_context; | 388 genesis = game_context; |
1306 genesis->m68k->options->address_log = address_log; | 389 genesis->m68k->options->address_log = address_log; |
1307 map_all_bindings(genesis->ports); | 390 map_all_bindings(genesis->ports); |
1308 start_genesis(genesis, statefile, menu == debug_target ? debuggerfun : NULL); | 391 start_genesis(genesis, statefile, menu == debug_target ? debuggerfun : NULL); |
1309 } else if (menu && game_context) { | 392 } else if (menu && game_context) { |
1310 genesis->arena = set_current_arena(game_context->arena); | 393 genesis->arena = set_current_arena(game_context->arena); |
1311 genesis = game_context; | 394 genesis = game_context; |
1312 cart = genesis->cart; | |
1313 ram = genesis->work_ram; | |
1314 menu = 0; | 395 menu = 0; |
1315 map_all_bindings(genesis->ports); | 396 map_all_bindings(genesis->ports); |
1316 resume_68k(genesis->m68k); | 397 resume_68k(genesis->m68k); |
1317 } else if (!menu && menu_context) { | 398 } else if (!menu && menu_context) { |
1318 genesis->arena = set_current_arena(menu_context->arena); | 399 genesis->arena = set_current_arena(menu_context->arena); |
1319 genesis = menu_context; | 400 genesis = menu_context; |
1320 cart = genesis->cart; | |
1321 ram = genesis->work_ram; | |
1322 menu = 1; | 401 menu = 1; |
1323 map_all_bindings(genesis->ports); | 402 map_all_bindings(genesis->ports); |
1324 resume_68k(genesis->m68k); | 403 resume_68k(genesis->m68k); |
1325 } else { | 404 } else { |
1326 break; | 405 break; |