annotate blastem.c @ 989:d70000fdff0b

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