# HG changeset patch # User Michael Pavone # Date 1481305728 28800 # Node ID 22e87b739ad652a992ad05a479f2ab68b1f9f7be # Parent c15896605bf2e5f5cebc0dc496e831e068826a1f 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. diff -r c15896605bf2 -r 22e87b739ad6 blastem.c --- a/blastem.c Mon Nov 28 22:45:46 2016 -0800 +++ b/blastem.c Fri Dec 09 09:48:48 2016 -0800 @@ -14,42 +14,23 @@ #include "mem.h" #include "vdp.h" #include "render.h" -#include "blastem.h" +#include "genesis.h" #include "gdb_remote.h" #include "gst.h" #include "util.h" #include "romdb.h" #include "terminal.h" #include "arena.h" +#include "config.h" #define BLASTEM_VERSION "0.4.1" -#define MCLKS_NTSC 53693175 -#define MCLKS_PAL 53203395 - -uint32_t MCLKS_PER_68K; -#define MCLKS_PER_YM 7 -#define MCLKS_PER_Z80 15 -#define MCLKS_PER_PSG (MCLKS_PER_Z80*16) -#define DEFAULT_SYNC_INTERVAL MCLKS_LINE -#define DEFAULT_LOWPASS_CUTOFF 3390 - -//TODO: Figure out the exact value for this -#define LINES_NTSC 262 -#define LINES_PAL 312 - -#define MAX_SOUND_CYCLES 100000 - #ifdef __ANDROID__ #define FULLSCREEN_DEFAULT 1 #else #define FULLSCREEN_DEFAULT 0 #endif -uint16_t *cart; -uint16_t *ram; -uint8_t z80_ram[Z80_RAM_BYTES]; - int headless = 0; int exit_after = 0; int z80_enabled = 1; @@ -67,13 +48,13 @@ #define SMD_MAGIC3 0xBB #define SMD_BLOCK_SIZE 0x4000 -int load_smd_rom(long filesize, FILE * f) +int load_smd_rom(long filesize, FILE * f, uint16_t **buffer) { uint8_t block[SMD_BLOCK_SIZE]; filesize -= SMD_HEADER_SIZE; fseek(f, SMD_HEADER_SIZE, SEEK_SET); - uint16_t * dst = cart = malloc(nearest_pow2(filesize)); + uint16_t *dst = *buffer = malloc(nearest_pow2(filesize)); int rom_size = filesize; while (filesize > 0) { fread(block, 1, SMD_BLOCK_SIZE, f); @@ -85,15 +66,7 @@ return rom_size; } -void byteswap_rom(int filesize) -{ - for(unsigned short * cur = cart; cur - cart < filesize/2; ++cur) - { - *cur = (*cur >> 8) | (*cur << 8); - } -} - -int load_rom(char * filename) +int load_rom(char * filename, uint16_t **dst) { uint8_t header[10]; FILE * f = fopen(filename, "rb"); @@ -117,742 +90,25 @@ if (header[2]) { fatal_error("%s is a split SMD ROM which is not currently supported", filename); } - return load_smd_rom(filesize, f); + return load_smd_rom(filesize, f, dst); } } - cart = malloc(nearest_pow2(filesize)); - if (filesize != fread(cart, 1, filesize, f)) { + *dst = malloc(nearest_pow2(filesize)); + if (filesize != fread(*dst, 1, filesize, f)) { fatal_error("Error reading from %s\n", filename); } fclose(f); return filesize; } -uint16_t read_dma_value(uint32_t address) -{ - //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2 - uint16_t *ptr = get_native_pointer(address*2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen); - if (ptr) { - return *ptr; - } - //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area - return 0; -} -uint16_t get_open_bus_value() -{ - return read_dma_value(genesis->m68k->last_prefetch_address/2); -} - -void adjust_int_cycle(m68k_context * context, vdp_context * v_context) -{ - //static int old_int_cycle = CYCLE_NEVER; - genesis_context *gen = context->system; - if (context->sync_cycle - context->current_cycle > gen->max_cycles) { - context->sync_cycle = context->current_cycle + gen->max_cycles; - } - context->int_cycle = CYCLE_NEVER; - if ((context->status & 0x7) < 6) { - uint32_t next_vint = vdp_next_vint(v_context); - if (next_vint != CYCLE_NEVER) { - context->int_cycle = next_vint; - context->int_num = 6; - } - if ((context->status & 0x7) < 4) { - uint32_t next_hint = vdp_next_hint(v_context); - if (next_hint != CYCLE_NEVER) { - next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; - if (next_hint < context->int_cycle) { - context->int_cycle = next_hint; - context->int_num = 4; - - } - } - } - } - if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { - context->int_pending = INT_PENDING_NONE; - } - /*if (context->int_cycle != old_int_cycle) { - 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); - old_int_cycle = context->int_cycle; - }*/ - - context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; - if (context->should_return) { - context->target_cycle = context->current_cycle; - } else if (context->target_cycle < context->current_cycle) { - //Changes to SR can result in an interrupt cycle that's in the past - //This can cause issues with the implementation of STOP though - context->target_cycle = context->current_cycle; - } - /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", - context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), - 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);*/ -} int break_on_sync = 0; char *save_state_path; -//#define DO_DEBUG_PRINT -#ifdef DO_DEBUG_PRINT -#define dprintf printf -#define dputs puts -#else -#define dprintf -#define dputs -#endif - -#define Z80_VINT_DURATION 128 - -void z80_next_int_pulse(z80_context * z_context) -{ - genesis_context * gen = z_context->system; - z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp); - z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80; - } - -void sync_z80(z80_context * z_context, uint32_t mclks) -{ -#ifndef NO_Z80 - if (z80_enabled) { - z80_run(z_context, mclks); - } else -#endif - { - z_context->current_cycle = mclks; - } -} - -void sync_sound(genesis_context * gen, uint32_t target) -{ - //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); - while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { - uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; - //printf("Running PSG to cycle %d\n", cur_target); - psg_run(gen->psg, cur_target); - //printf("Running YM-2612 to cycle %d\n", cur_target); - ym_run(gen->ym, cur_target); - } - psg_run(gen->psg, target); - ym_run(gen->ym, target); - - //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); -} - -uint32_t last_frame_num; - -//My refresh emulation isn't currently good enough and causes more problems than it solves -#ifdef REFRESH_EMULATION -#define REFRESH_INTERVAL 128 -#define REFRESH_DELAY 2 -uint32_t last_sync_cycle; -uint32_t refresh_counter; -#endif - -m68k_context * sync_components(m68k_context * context, uint32_t address) -{ - genesis_context * gen = context->system; - vdp_context * v_context = gen->vdp; - z80_context * z_context = gen->z80; -#ifdef REFRESH_EMULATION - //lame estimation of refresh cycle delay - if (!gen->bus_busy) { - refresh_counter += context->current_cycle - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - } -#endif - - uint32_t mclks = context->current_cycle; - sync_z80(z_context, mclks); - sync_sound(gen, mclks); - vdp_run_context(v_context, mclks); - if (v_context->frame != last_frame_num) { - //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); - last_frame_num = v_context->frame; - - if(exit_after){ - --exit_after; - if (!exit_after) { - exit(0); - } - } - - vdp_adjust_cycles(v_context, mclks); - io_adjust_cycles(gen->ports, context->current_cycle, mclks); - io_adjust_cycles(gen->ports+1, context->current_cycle, mclks); - io_adjust_cycles(gen->ports+2, context->current_cycle, mclks); - context->current_cycle -= mclks; - z80_adjust_cycles(z_context, mclks); - gen->ym->current_cycle -= mclks; - gen->psg->cycles -= mclks; - if (gen->ym->write_cycle != CYCLE_NEVER) { - gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0; - } - } - gen->frame_end = vdp_cycles_to_frame_end(v_context); - context->sync_cycle = gen->frame_end; - //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); - if (context->int_ack) { - //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); - vdp_int_ack(v_context); - context->int_ack = 0; - } - if (!address && (break_on_sync || gen->save_state)) { - context->sync_cycle = context->current_cycle + 1; - } - adjust_int_cycle(context, v_context); - if (address) { - if (break_on_sync) { - break_on_sync = 0; - debugger(context, address); - } - if (gen->save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) { - uint8_t slot = gen->save_state - 1; - gen->save_state = 0; - //advance Z80 core to the start of an instruction - while (!z_context->pc) - { - sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80); - } - char *save_path; - if (slot == QUICK_SAVE_SLOT) { - save_path = save_state_path; - } else { - char slotname[] = "slot_0.gst"; - slotname[5] = '0' + slot; - char const *parts[] = {gen->save_dir, PATH_SEP, slotname}; - save_path = alloc_concat_m(3, parts); - } - save_gst(gen, save_path, address); - printf("Saved state to %s\n", save_path); - if (slot != QUICK_SAVE_SLOT) { - free(save_path); - } - } else if(gen->save_state) { - context->sync_cycle = context->current_cycle + 1; - } - } -#ifdef REFRESH_EMULATION - last_sync_cycle = context->current_cycle; -#endif - return context; -} - -m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) -{ - if (vdp_port & 0x2700E0) { - fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port); - } - vdp_port &= 0x1F; - //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); - sync_components(context, 0); - genesis_context * gen = context->system; - vdp_context *v_context = gen->vdp; - if (vdp_port < 0x10) { - int blocked; - uint32_t before_cycle = v_context->cycles; - if (vdp_port < 4) { - - while (vdp_data_port_write(v_context, value) < 0) { - while(v_context->flags & FLAG_DMA_RUN) { - vdp_run_dma_done(v_context, gen->frame_end); - if (v_context->cycles >= gen->frame_end) { - uint32_t cycle_diff = v_context->cycles - context->current_cycle; - uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; - if (m68k_cycle_diff < cycle_diff) { - m68k_cycle_diff += MCLKS_PER_68K; - } - context->current_cycle += m68k_cycle_diff; - gen->bus_busy = 1; - sync_components(context, 0); - gen->bus_busy = 0; - } - } - //context->current_cycle = v_context->cycles; - } - } else if(vdp_port < 8) { - blocked = vdp_control_port_write(v_context, value); - if (blocked) { - while (blocked) { - while(v_context->flags & FLAG_DMA_RUN) { - vdp_run_dma_done(v_context, gen->frame_end); - if (v_context->cycles >= gen->frame_end) { - uint32_t cycle_diff = v_context->cycles - context->current_cycle; - uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; - if (m68k_cycle_diff < cycle_diff) { - m68k_cycle_diff += MCLKS_PER_68K; - } - context->current_cycle += m68k_cycle_diff; - gen->bus_busy = 1; - sync_components(context, 0); - gen->bus_busy = 0; - } - if (!(v_context->flags & FLAG_DMA_RUN)) { - //two more slots of delay are needed to kill sufficient sprite capacity in Overdrive - //TODO: Measure exact value with logic analyzer - vdp_run_context(v_context, v_context->cycles + 1); - vdp_run_context(v_context, v_context->cycles + 1); - } - } - - if (blocked < 0) { - blocked = vdp_control_port_write(v_context, value); - } else { - blocked = 0; - } - } - } else { - context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context); - //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); - adjust_int_cycle(context, v_context); - } - } else { - fatal_error("Illegal write to HV Counter port %X\n", vdp_port); - } - if (v_context->cycles != before_cycle) { - //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); - uint32_t cycle_diff = v_context->cycles - context->current_cycle; - uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; - if (m68k_cycle_diff < cycle_diff) { - m68k_cycle_diff += MCLKS_PER_68K; - } - context->current_cycle += m68k_cycle_diff; -#ifdef REFRESH_EMULATION - last_sync_cycle = context->current_cycle; -#endif - //Lock the Z80 out of the bus until the VDP access is complete - gen->bus_busy = 1; - sync_z80(gen->z80, v_context->cycles); - gen->bus_busy = 0; - } - } else if (vdp_port < 0x18) { - psg_write(gen->psg, value); - } else { - //TODO: Implement undocumented test register(s) - } - return context; -} - -m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value) -{ - return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0)); -} - -void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value) -{ - z80_context * context = vcontext; - genesis_context * gen = context->system; - vdp_port &= 0xFF; - if (vdp_port & 0xE0) { - fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port); - } - if (vdp_port < 0x10) { - //These probably won't currently interact well with the 68K accessing the VDP - vdp_run_context(gen->vdp, context->current_cycle); - if (vdp_port < 4) { - vdp_data_port_write(gen->vdp, value << 8 | value); - } else if (vdp_port < 8) { - vdp_control_port_write(gen->vdp, value << 8 | value); - } else { - fatal_error("Illegal write to HV Counter port %X\n", vdp_port); - } - } else if (vdp_port < 0x18) { - sync_sound(gen, context->current_cycle); - psg_write(gen->psg, value); - } else { - vdp_test_port_write(gen->vdp, value); - } - return context; -} - -uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) -{ - if (vdp_port & 0x2700E0) { - fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port); - } - vdp_port &= 0x1F; - uint16_t value; - sync_components(context, 0); - genesis_context *gen = context->system; - vdp_context * v_context = gen->vdp; - uint32_t before_cycle = v_context->cycles; - if (vdp_port < 0x10) { - if (vdp_port < 4) { - value = vdp_data_port_read(v_context); - } else if(vdp_port < 8) { - value = vdp_control_port_read(v_context); - } else { - value = vdp_hv_counter_read(v_context); - //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); - } - } else if (vdp_port < 0x18){ - fatal_error("Illegal read from PSG port %X\n", vdp_port); - } else { - value = vdp_test_port_read(v_context); - } - if (v_context->cycles != before_cycle) { - //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); - context->current_cycle = v_context->cycles; -#ifdef REFRES_EMULATION - last_sync_cycle = context->current_cycle; -#endif - //Lock the Z80 out of the bus until the VDP access is complete - genesis_context *gen = context->system; - gen->bus_busy = 1; - sync_z80(gen->z80, v_context->cycles); - gen->bus_busy = 0; - } - return value; -} - -uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context) -{ - uint16_t value = vdp_port_read(vdp_port, context); - if (vdp_port & 1) { - return value; - } else { - return value >> 8; - } -} - -uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext) -{ - z80_context * context = vcontext; - if (vdp_port & 0xE0) { - fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port); - } - genesis_context * gen = context->system; - //VDP access goes over the 68K bus like a bank area access - //typical delay from bus arbitration - context->current_cycle += 3 * MCLKS_PER_Z80; - //TODO: add cycle for an access right after a previous one - //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high - // Needs a new logic analyzer capture to get the actual delay on the 68K side - gen->m68k->current_cycle += 8 * MCLKS_PER_68K; - vdp_port &= 0x1F; - uint16_t ret; - if (vdp_port < 0x10) { - //These probably won't currently interact well with the 68K accessing the VDP - vdp_run_context(gen->vdp, context->current_cycle); - if (vdp_port < 4) { - ret = vdp_data_port_read(gen->vdp); - } else if (vdp_port < 8) { - ret = vdp_control_port_read(gen->vdp); - } else { - fatal_error("Illegal write to HV Counter port %X\n", vdp_port); - } - } else { - //TODO: Figure out the correct value today - ret = 0xFFFF; - } - return vdp_port & 1 ? ret : ret >> 8; -} -uint32_t zram_counter = 0; - -m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) -{ - genesis_context * gen = context->system; - if (location < 0x10000) { - //Access to Z80 memory incurs a one 68K cycle wait state - context->current_cycle += MCLKS_PER_68K; - if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { - location &= 0x7FFF; - if (location < 0x4000) { - z80_ram[location & 0x1FFF] = value; -#ifndef NO_Z80 - z80_handle_code_write(location & 0x1FFF, gen->z80); -#endif - } else if (location < 0x6000) { - sync_sound(gen, context->current_cycle); - if (location & 1) { - ym_data_write(gen->ym, value); - } else if(location & 2) { - ym_address_write_part2(gen->ym, value); - } else { - ym_address_write_part1(gen->ym, value); - } - } else if (location == 0x6000) { - gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF; - if (gen->z80->bank_reg < 0x80) { - gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]); - } else { - gen->z80->mem_pointers[1] = NULL; - } - } else { - fatal_error("68K write to unhandled Z80 address %X\n", location); - } - } - } else { - location &= 0x1FFF; - if (location < 0x100) { - switch(location/2) - { - case 0x1: - io_data_write(gen->ports, value, context->current_cycle); - break; - case 0x2: - io_data_write(gen->ports+1, value, context->current_cycle); - break; - case 0x3: - io_data_write(gen->ports+2, value, context->current_cycle); - break; - case 0x4: - gen->ports[0].control = value; - break; - case 0x5: - gen->ports[1].control = value; - break; - case 0x6: - gen->ports[2].control = value; - break; - } - } else { - if (location == 0x1100) { - if (value & 1) { - dputs("bus requesting Z80"); - if (z80_enabled) { - z80_assert_busreq(gen->z80, context->current_cycle); - } else { - gen->z80->busack = 1; - } - } else { - if (gen->z80->busreq) { - dputs("releasing z80 bus"); - #ifdef DO_DEBUG_PRINT - char fname[20]; - sprintf(fname, "zram-%d", zram_counter++); - FILE * f = fopen(fname, "wb"); - fwrite(z80_ram, 1, sizeof(z80_ram), f); - fclose(f); - #endif - } - if (z80_enabled) { - z80_clear_busreq(gen->z80, context->current_cycle); - } else { - gen->z80->busack = 0; - } - } - } else if (location == 0x1200) { - sync_z80(gen->z80, context->current_cycle); - if (value & 1) { - if (z80_enabled) { - z80_clear_reset(gen->z80, context->current_cycle); - } else { - gen->z80->reset = 0; - } - } else { - if (z80_enabled) { - z80_assert_reset(gen->z80, context->current_cycle); - } else { - gen->z80->reset = 1; - } - } - } - } - } - return context; -} - -m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) -{ - if (location < 0x10000 || (location & 0x1FFF) >= 0x100) { - return io_write(location, context, value >> 8); - } else { - return io_write(location, context, value); - } -} - -#define USA 0x80 -#define JAP 0x00 -#define EUR 0xC0 -#define NO_DISK 0x20 -uint8_t version_reg = NO_DISK | USA; - -uint8_t io_read(uint32_t location, m68k_context * context) -{ - uint8_t value; - genesis_context *gen = context->system; - if (location < 0x10000) { - //Access to Z80 memory incurs a one 68K cycle wait state - context->current_cycle += MCLKS_PER_68K; - if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { - location &= 0x7FFF; - if (location < 0x4000) { - value = z80_ram[location & 0x1FFF]; - } else if (location < 0x6000) { - sync_sound(gen, context->current_cycle); - value = ym_read_status(gen->ym); - } else { - value = 0xFF; - } - } else { - value = 0xFF; - } - } else { - location &= 0x1FFF; - if (location < 0x100) { - switch(location/2) - { - case 0x0: - //version bits should be 0 for now since we're not emulating TMSS - value = version_reg; - break; - case 0x1: - value = io_data_read(gen->ports, context->current_cycle); - break; - case 0x2: - value = io_data_read(gen->ports+1, context->current_cycle); - break; - case 0x3: - value = io_data_read(gen->ports+2, context->current_cycle); - break; - case 0x4: - value = gen->ports[0].control; - break; - case 0x5: - value = gen->ports[1].control; - break; - case 0x6: - value = gen->ports[2].control; - break; - default: - value = 0xFF; - } - } else { - if (location == 0x1100) { - value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack; - value |= (get_open_bus_value() >> 8) & 0xFE; - dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset); - } else if (location == 0x1200) { - value = !gen->z80->reset; - } else { - value = 0xFF; - printf("Byte read of unknown IO location: %X\n", location); - } - } - } - return value; -} - -uint16_t io_read_w(uint32_t location, m68k_context * context) -{ - uint16_t value = io_read(location, context); - if (location < 0x10000 || (location & 0x1FFF) < 0x100) { - value = value | (value << 8); - } else { - value <<= 8; - value |= get_open_bus_value() & 0xFF; - } - return value; -} - -void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value) -{ - z80_context * context = vcontext; - genesis_context * gen = context->system; - sync_sound(gen, context->current_cycle); - if (location & 1) { - ym_data_write(gen->ym, value); - } else if (location & 2) { - ym_address_write_part2(gen->ym, value); - } else { - ym_address_write_part1(gen->ym, value); - } - return context; -} - -uint8_t z80_read_ym(uint32_t location, void * vcontext) -{ - z80_context * context = vcontext; - genesis_context * gen = context->system; - sync_sound(gen, context->current_cycle); - return ym_read_status(gen->ym); -} - -uint8_t z80_read_bank(uint32_t location, void * vcontext) -{ - z80_context * context = vcontext; - genesis_context *gen = context->system; - if (gen->bus_busy) { - context->current_cycle = context->sync_cycle; - } - //typical delay from bus arbitration - context->current_cycle += 3 * MCLKS_PER_Z80; - //TODO: add cycle for an access right after a previous one - //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high - // Needs a new logic analyzer capture to get the actual delay on the 68K side - gen->m68k->current_cycle += 8 * MCLKS_PER_68K; - - location &= 0x7FFF; - if (context->mem_pointers[1]) { - return context->mem_pointers[1][location ^ 1]; - } - uint32_t address = context->bank_reg << 15 | location; - if (address >= 0xC00000 && address < 0xE00000) { - return z80_vdp_port_read(location & 0xFF, context); - } else { - fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15); - } - return 0; -} - -void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value) -{ - z80_context * context = vcontext; - genesis_context *gen = context->system; - if (gen->bus_busy) { - context->current_cycle = context->sync_cycle; - } - //typical delay from bus arbitration - context->current_cycle += 3 * MCLKS_PER_Z80; - //TODO: add cycle for an access right after a previous one - //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high - // Needs a new logic analyzer capture to get the actual delay on the 68K side - gen->m68k->current_cycle += 8 * MCLKS_PER_68K; - - location &= 0x7FFF; - uint32_t address = context->bank_reg << 15 | location; - if (address >= 0xE00000) { - address &= 0xFFFF; - ((uint8_t *)ram)[address ^ 1] = value; - } else if (address >= 0xC00000) { - z80_vdp_port_write(location & 0xFF, context, value); - } else { - fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address); - } - return context; -} - -void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value) -{ - z80_context * context = vcontext; - - context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF; - if (context->bank_reg < 0x100) { - genesis_context *gen = context->system; - context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen); - } else { - context->mem_pointers[1] = NULL; - } - - return context; -} - -void set_speed_percent(genesis_context * context, uint32_t percent) -{ - uint32_t old_clock = context->master_clock; - context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; - while (context->ym->current_cycle != context->psg->cycles) { - sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); -} - ym_adjust_master_clock(context->ym, context->master_clock); - psg_adjust_master_clock(context->psg, context->master_clock); -} char * save_filename; genesis_context *genesis; @@ -873,124 +129,9 @@ printf("Saved %s to %s\n", game_context->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename); } -#ifndef NO_Z80 -const memmap_chunk z80_map[] = { - { 0x0000, 0x4000, 0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, z80_ram, NULL, NULL, NULL, NULL }, - { 0x8000, 0x10000, 0x7FFF, 0, 0, 0, NULL, NULL, NULL, z80_read_bank, z80_write_bank}, - { 0x4000, 0x6000, 0x0003, 0, 0, 0, NULL, NULL, NULL, z80_read_ym, z80_write_ym}, - { 0x6000, 0x6100, 0xFFFF, 0, 0, 0, NULL, NULL, NULL, NULL, z80_write_bank_reg}, - { 0x7F00, 0x8000, 0x00FF, 0, 0, 0, NULL, NULL, NULL, z80_vdp_port_read, z80_vdp_port_write} -}; -#endif -genesis_context *alloc_init_genesis(rom_info *rom, int fps, uint32_t ym_opts) -{ - genesis_context *gen = calloc(1, sizeof(genesis_context)); - gen->master_clock = gen->normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL; - - gen->vdp = malloc(sizeof(vdp_context)); - init_vdp_context(gen->vdp, version_reg & 0x40); - gen->frame_end = vdp_cycles_to_frame_end(gen->vdp); - char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval; - gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL; - - char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval; - uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF; - - gen->ym = malloc(sizeof(ym2612_context)); - ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff); - - gen->psg = malloc(sizeof(psg_context)); - psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff); - - gen->z80 = calloc(1, sizeof(z80_context)); -#ifndef NO_Z80 - z80_options *z_opts = malloc(sizeof(z80_options)); - init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80); - init_z80_context(gen->z80, z_opts); - z80_assert_reset(gen->z80, 0); -#endif - - gen->z80->system = gen; - gen->z80->mem_pointers[0] = z80_ram; - gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart; - - gen->cart = cart; - gen->work_ram = ram; - gen->zram = z80_ram; - setup_io_devices(config, rom, gen); - - gen->save_type = rom->save_type; - gen->save_type = rom->save_type; - if (gen->save_type != SAVE_NONE) { - gen->save_ram_mask = rom->save_mask; - gen->save_size = rom->save_size; - gen->save_storage = rom->save_buffer; - gen->eeprom_map = rom->eeprom_map; - gen->num_eeprom = rom->num_eeprom; - if (gen->save_type == SAVE_I2C) { - eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); - } - } else { - gen->save_storage = NULL; - } - - m68k_options *opts = malloc(sizeof(m68k_options)); - init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K); - //TODO: make this configurable - opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; - gen->m68k = init_68k_context(opts, NULL); - gen->m68k->system = gen; - - for (int i = 0; i < rom->map_chunks; i++) - { - if (rom->map[i].flags & MMAP_PTR_IDX) { - gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; - } - } - - return gen; -} - -void free_genesis(genesis_context *gen) -{ - vdp_free(gen->vdp); - m68k_options_free(gen->m68k->options); - free(gen->m68k); - z80_options_free(gen->z80->options); - free(gen->z80); - ym_free(gen->ym); - psg_free(gen->psg); - free(gen->save_storage); - free(gen->save_dir); - free(gen->lock_on); -} - -void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger) -{ - - if (statefile) { - uint32_t pc = load_gst(gen, statefile); - if (!pc) { - fatal_error("Failed to load save state %s\n", statefile); - } - printf("Loaded %s\n", statefile); - if (debugger) { - insert_breakpoint(gen->m68k, pc, debugger); - } - adjust_int_cycle(gen->m68k, gen->vdp); - start_68k_context(gen->m68k, pc); - } else { - if (debugger) { - uint32_t address = cart[2] << 16 | cart[3]; - insert_breakpoint(gen->m68k, address, debugger); - } - m68k_reset(gen->m68k); - } -} char *title; - void update_title(char *rom_name) { if (title) { @@ -1001,25 +142,6 @@ render_update_caption(title); } -void set_region(rom_info *info, uint8_t region) -{ - if (!region) { - char * def_region = tern_find_ptr(config, "default_region"); - if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) { - region = translate_region_char(toupper(*def_region)); - } else { - region = info->regions; - } - } - if (region & REGION_E) { - version_reg = NO_DISK | EUR; - } else if (region & REGION_J) { - version_reg = NO_DISK | JAP; - } else { - version_reg = NO_DISK | USA; - } -} - void setup_saves(char *fname, rom_info *info, genesis_context *context) { char * barename = basename_no_extension(fname); @@ -1059,12 +181,12 @@ int debug = 0; int ym_log = 0; int loaded = 0; - uint8_t force_version = 0; + uint8_t force_region = 0; char * romfname = NULL; FILE *address_log = NULL; char * statefile = NULL; int rom_size, lock_on_size; - uint16_t *lock_on = NULL; + uint16_t *cart = NULL, *lock_on = NULL; uint8_t * debuggerfun = NULL; uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1; uint8_t debug_target = 0; @@ -1111,8 +233,8 @@ if (i >= argc) { fatal_error("-r must be followed by region (J, U or E)\n"); } - force_version = translate_region_char(toupper(argv[i][0])); - if (!force_version) { + force_region = translate_region_char(toupper(argv[i][0])); + if (!force_region) { fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]); } break; @@ -1134,15 +256,10 @@ if (i >= argc) { fatal_error("-o must be followed by a lock on cartridge filename\n"); } - uint16_t *tmp = cart; - lock_on_size = load_rom(argv[i]); - if (lock_on_size) { - byteswap_rom(lock_on_size); - lock_on = cart; - } else { + lock_on_size = load_rom(argv[i], &lock_on); + if (!lock_on_size) { fatal_error("Failed to load lock on cartridge %s\n", argv[i]); } - cart = tmp; break; } case 'h': @@ -1166,7 +283,7 @@ fatal_error("Unrecognized switch %s\n", argv[i]); } } else if (!loaded) { - if (!(rom_size = load_rom(argv[i]))) { + if (!(rom_size = load_rom(argv[i], &cart))) { fatal_error("Failed to open %s for reading\n", argv[i]); } romfname = argv[i]; @@ -1185,7 +302,7 @@ romfname = "menu.bin"; } if (is_absolute_path(romfname)) { - if (!(rom_size = load_rom(romfname))) { + if (!(rom_size = load_rom(romfname, &cart))) { fatal_error("Failed to open UI ROM %s for reading", romfname); } } else { @@ -1199,34 +316,10 @@ cart = realloc(cart, rom_size); } } - //TODO: load relative to executable or from assets depending on platform loaded = 1; } - char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval; - if (!m68k_divider) { - m68k_divider = "7"; - } - MCLKS_PER_68K = atoi(m68k_divider); - if (!MCLKS_PER_68K) { - MCLKS_PER_68K = 7; - } - ram = malloc(RAM_WORDS * sizeof(uint16_t)); - memmap_chunk base_map[] = { - {0xE00000, 0x1000000, 0xFFFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, - NULL, NULL, NULL, NULL}, - {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, 0, NULL, - (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, - (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, - {0xA00000, 0xA12000, 0x1FFFF, 0, 0, 0, NULL, - (read_16_fun)io_read_w, (write_16_fun)io_write_w, - (read_8_fun)io_read, (write_8_fun)io_write} - }; - tern_node *rom_db = load_rom_db(); - rom_info info = configure_rom(rom_db, cart, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0])); - byteswap_rom(rom_size); - set_region(&info, force_version); - update_title(info.name); + int def_width = 0; char *config_width = tern_find_path(config, "video\0width\0").ptrval; if (config_width) { @@ -1237,21 +330,20 @@ } width = width < 320 ? def_width : width; height = height < 240 ? (width/320) * 240 : height; - uint32_t fps = 60; - if (version_reg & 0x40) { - fps = 50; - } + char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0").ptrval; if (config_fullscreen && !strcmp("on", config_fullscreen)) { fullscreen = !fullscreen; } if (!headless) { - render_init(width, height, title, fps, fullscreen); + render_init(width, height, "BlastEm", fullscreen); } - genesis = alloc_init_genesis(&info, fps, (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0); - genesis->lock_on = lock_on; + rom_info info; + uint32_t ym_opts = (ym_log && !menu) ? YM_OPT_WAVE_LOG : 0; + genesis = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts, force_region, &info); setup_saves(romfname, &info, genesis); + update_title(info.name); if (menu) { menu_context = genesis; } else { @@ -1273,32 +365,23 @@ persist_save(); genesis = menu_context; } - free(game_context->cart); - base_map[0].buffer = ram = game_context->work_ram; - } else { - base_map[0].buffer = ram = malloc(RAM_WORDS * sizeof(uint16_t)); - } - memset(ram, 0, RAM_WORDS * sizeof(uint16_t)); - if (!(rom_size = load_rom(menu_context->next_rom))) { - fatal_error("Failed to open %s for reading\n", menu_context->next_rom); - } - info = configure_rom(rom_db, cart, rom_size, NULL, 0, base_map, sizeof(base_map)/sizeof(base_map[0])); - byteswap_rom(rom_size); - set_region(&info, force_version); - update_title(info.name); - if (!game_context) { - //start a new arena and save old one in suspended genesis context - genesis->arena = start_new_arena(); - } else { + //swap to game context arena and mark all allocated pages in it free genesis->arena = set_current_arena(game_context->arena); mark_all_free(); free_genesis(game_context); + } else { + //start a new arena and save old one in suspended genesis context + genesis->arena = start_new_arena(); + } + if (!(rom_size = load_rom(menu_context->next_rom, &cart))) { + fatal_error("Failed to open %s for reading\n", menu_context->next_rom); } //allocate new genesis context - game_context = alloc_init_genesis(&info, fps, ym_log ? YM_OPT_WAVE_LOG : 0); + game_context = alloc_config_genesis(cart, rom_size, lock_on, lock_on_size, ym_opts,force_region, &info); menu_context->next_context = game_context; game_context->next_context = menu_context; setup_saves(menu_context->next_rom, &info, game_context); + update_title(info.name); free(menu_context->next_rom); menu_context->next_rom = NULL; menu = 0; @@ -1309,16 +392,12 @@ } else if (menu && game_context) { genesis->arena = set_current_arena(game_context->arena); genesis = game_context; - cart = genesis->cart; - ram = genesis->work_ram; menu = 0; map_all_bindings(genesis->ports); resume_68k(genesis->m68k); } else if (!menu && menu_context) { genesis->arena = set_current_arena(menu_context->arena); genesis = menu_context; - cart = genesis->cart; - ram = genesis->work_ram; menu = 1; map_all_bindings(genesis->ports); resume_68k(genesis->m68k); diff -r c15896605bf2 -r 22e87b739ad6 blastem.h --- a/blastem.h Mon Nov 28 22:45:46 2016 -0800 +++ b/blastem.h Fri Dec 09 09:48:48 2016 -0800 @@ -1,78 +1,16 @@ -/* - Copyright 2013 Michael Pavone - This file is part of BlastEm. - BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. -*/ #ifndef BLASTEM_H_ #define BLASTEM_H_ -#include -#include "m68k_core.h" -#include "z80_to_x86.h" -#include "ym2612.h" -#include "vdp.h" -#include "psg.h" -#include "io.h" -#include "config.h" -#include "romdb.h" -#include "arena.h" - -typedef struct genesis_context genesis_context; +#include "tern.h" -struct genesis_context { - m68k_context *m68k; - z80_context *z80; - vdp_context *vdp; - ym2612_context *ym; - psg_context *psg; - genesis_context *next_context; - uint16_t *cart; - uint16_t *lock_on; - uint16_t *work_ram; - uint8_t *zram; - void *extra; - arena *arena; - char *next_rom; - char *save_dir; - uint8_t *save_storage; - eeprom_map *eeprom_map; - uint32_t num_eeprom; - uint32_t save_size; - uint32_t save_ram_mask; - uint32_t master_clock; //Current master clock value - uint32_t normal_clock; //Normal master clock (used to restore master clock after turbo mode) - uint32_t frame_end; - uint32_t max_cycles; - uint8_t bank_regs[8]; - uint16_t mapper_start_index; - uint8_t save_type; - io_port ports[3]; - uint8_t bus_busy; - uint8_t should_exit; - uint8_t save_state; - uint8_t mouse_mode; - uint8_t mouse_captured; - eeprom_state eeprom; -}; +extern int headless; +extern int exit_after; +extern int z80_enabled; +extern int frame_limit; -extern genesis_context * genesis; -extern int headless; -extern int break_on_sync; extern tern_node * config; -#define RAM_WORDS 32 * 1024 -#define Z80_RAM_BYTES 8 * 1024 +extern char *save_state_path; #define QUICK_SAVE_SLOT 10 -extern uint16_t *cart; -extern uint16_t *ram; -extern uint8_t z80_ram[Z80_RAM_BYTES]; - -uint16_t read_dma_value(uint32_t address); -uint16_t get_open_bus_value(); -m68k_context * sync_components(m68k_context *context, uint32_t address); -m68k_context * debugger(m68k_context * context, uint32_t address); -void set_speed_percent(genesis_context * context, uint32_t percent); - #endif //BLASTEM_H_ - diff -r c15896605bf2 -r 22e87b739ad6 debug.c --- a/debug.c Mon Nov 28 22:45:46 2016 -0800 +++ b/debug.c Fri Dec 09 09:48:48 2016 -0800 @@ -1,5 +1,5 @@ #include "debug.h" -#include "blastem.h" +#include "genesis.h" #include "68kinst.h" #include #include @@ -140,6 +140,7 @@ uint32_t value; char format[8]; strcpy(format, "%s: %d\n"); + genesis_context *system = context->system; switch (format_char) { case 'x': @@ -305,14 +306,14 @@ if (param[1] == 'x') { uint16_t p_addr = strtol(param+2, NULL, 16); if (p_addr < 0x4000) { - value = z80_ram[p_addr & 0x1FFF]; + value = system->zram[p_addr & 0x1FFF]; } else if(p_addr >= 0x8000) { uint32_t v_addr = context->bank_reg << 15; v_addr += p_addr & 0x7FFF; if (v_addr < 0x400000) { - value = cart[v_addr/2]; + value = system->cart[v_addr/2]; } else if(v_addr > 0xE00000) { - value = ram[(v_addr & 0xFFFF)/2]; + value = system->work_ram[(v_addr & 0xFFFF)/2]; } if (v_addr & 1) { value &= 0xFF; @@ -333,6 +334,7 @@ static uint16_t branch_t; static uint16_t branch_f; z80inst inst; + genesis_context *system = context->system; init_terminal(); //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&zbreakpoints, address); @@ -343,7 +345,7 @@ } uint8_t * pc; if (address < 0x4000) { - pc = z80_ram + (address & 0x1FFF); + pc = system->zram + (address & 0x1FFF); } else if (address >= 0x8000) { if (context->bank_reg < (0x400000 >> 15)) { fatal_error("Entered Z80 debugger in banked memory address %X, which is not yet supported\n", address); @@ -469,7 +471,7 @@ after += inst.immed; } else if(inst.op == Z80_RET) { if (context->sp < 0x4000) { - after = z80_ram[context->sp & 0x1FFF] | z80_ram[(context->sp+1) & 0x1FFF] << 8; + after = system->zram[context->sp & 0x1FFF] | system->zram[(context->sp+1) & 0x1FFF] << 8; } } zinsert_breakpoint(context, after, (uint8_t *)zdebugger); @@ -495,7 +497,7 @@ } FILE * f = fopen(param, "wb"); if (f) { - if(fwrite(z80_ram, 1, sizeof(z80_ram), f) != sizeof(z80_ram)) { + if(fwrite(system->zram, 1, Z80_RAM_BYTES, f) != Z80_RAM_BYTES) { fputs("Error writing file\n", stderr); } fclose(f); @@ -521,6 +523,7 @@ { char * param; char format_char; + genesis_context *system = context->system; uint32_t value; bp_def *new_bp, **this_bp; switch(input_buf[0]) @@ -571,16 +574,16 @@ stack &= 0xFFFF; uint8_t non_adr_count = 0; do { - uint32_t bt_address = ram[stack/2] << 16 | ram[stack/2+1]; + uint32_t bt_address = system->work_ram[stack/2] << 16 | system->work_ram[stack/2+1]; bt_address = get_instruction_start(context->options, context->native_code_map, bt_address - 2); if (bt_address) { stack += 4; non_adr_count = 0; uint16_t *bt_pc = NULL; if (bt_address < 0x400000) { - bt_pc = cart + bt_address/2; + bt_pc = system->cart + bt_address/2; } else if(bt_address > 0xE00000) { - bt_pc = ram + (bt_address & 0xFFFF)/2; + bt_pc = system->work_ram + (bt_address & 0xFFFF)/2; } m68k_decode(bt_pc, &inst, bt_address); m68k_disasm(&inst, input_buf); @@ -851,12 +854,8 @@ branch_t = branch_f = 0; } - uint16_t * pc; - if (address < 0x400000) { - pc = cart + address/2; - } else if(address > 0xE00000) { - pc = ram + (address & 0xFFFF)/2; - } else { + uint16_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->options->gen); + if (!pc) { fatal_error("Entered 68K debugger at address %X\n", address); } uint16_t * after_pc = m68k_decode(pc, &inst, address); diff -r c15896605bf2 -r 22e87b739ad6 gdb_remote.c --- a/gdb_remote.c Mon Nov 28 22:45:46 2016 -0800 +++ b/gdb_remote.c Fri Dec 09 09:48:48 2016 -0800 @@ -168,7 +168,7 @@ return; } if (address >= 0xA00000 && address < 0xA04000) { - z80_ram[address & 0x1FFF] = value; + gen->zram[address & 0x1FFF] = value; genesis_context * gen = context->system; #ifndef NO_Z80 z80_handle_code_write(address & 0x1FFF, gen->z80); diff -r c15896605bf2 -r 22e87b739ad6 gdb_remote.h --- a/gdb_remote.h Mon Nov 28 22:45:46 2016 -0800 +++ b/gdb_remote.h Fri Dec 09 09:48:48 2016 -0800 @@ -1,6 +1,6 @@ #ifndef GDB_REMOTE_H_ #define GDB_REMOTE_H_ -#include "blastem.h" +#include "genesis.h" void gdb_remote_init(void); m68k_context * gdb_debug_enter(m68k_context * context, uint32_t pc); diff -r c15896605bf2 -r 22e87b739ad6 genesis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genesis.c Fri Dec 09 09:48:48 2016 -0800 @@ -0,0 +1,936 @@ +/* + Copyright 2013-2016 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "genesis.h" +#include "blastem.h" +#include +#include "render.h" +#include "gst.h" +#include "util.h" +#define MCLKS_NTSC 53693175 +#define MCLKS_PAL 53203395 + +uint32_t MCLKS_PER_68K; +#define MCLKS_PER_YM 7 +#define MCLKS_PER_Z80 15 +#define MCLKS_PER_PSG (MCLKS_PER_Z80*16) +#define DEFAULT_SYNC_INTERVAL MCLKS_LINE +#define DEFAULT_LOWPASS_CUTOFF 3390 + +//TODO: Figure out the exact value for this +#define LINES_NTSC 262 +#define LINES_PAL 312 + +#define MAX_SOUND_CYCLES 100000 + +uint16_t *cart; +uint16_t *ram; +uint8_t z80_ram[Z80_RAM_BYTES]; + +uint16_t read_dma_value(uint32_t address) +{ + //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2 + uint16_t *ptr = get_native_pointer(address*2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen); + if (ptr) { + return *ptr; + } + //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area + return 0; +} + +uint16_t get_open_bus_value() +{ + return read_dma_value(genesis->m68k->last_prefetch_address/2); +} + +void adjust_int_cycle(m68k_context * context, vdp_context * v_context) +{ + //static int old_int_cycle = CYCLE_NEVER; + genesis_context *gen = context->system; + if (context->sync_cycle - context->current_cycle > gen->max_cycles) { + context->sync_cycle = context->current_cycle + gen->max_cycles; + } + context->int_cycle = CYCLE_NEVER; + if ((context->status & 0x7) < 6) { + uint32_t next_vint = vdp_next_vint(v_context); + if (next_vint != CYCLE_NEVER) { + context->int_cycle = next_vint; + context->int_num = 6; + } + if ((context->status & 0x7) < 4) { + uint32_t next_hint = vdp_next_hint(v_context); + if (next_hint != CYCLE_NEVER) { + next_hint = next_hint < context->current_cycle ? context->current_cycle : next_hint; + if (next_hint < context->int_cycle) { + context->int_cycle = next_hint; + context->int_num = 4; + + } + } + } + } + if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { + context->int_pending = INT_PENDING_NONE; + } + /*if (context->int_cycle != old_int_cycle) { + 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); + old_int_cycle = context->int_cycle; + }*/ + + context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; + if (context->should_return) { + context->target_cycle = context->current_cycle; + } else if (context->target_cycle < context->current_cycle) { + //Changes to SR can result in an interrupt cycle that's in the past + //This can cause issues with the implementation of STOP though + context->target_cycle = context->current_cycle; + } + /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", + context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), + 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);*/ +} + +//#define DO_DEBUG_PRINT +#ifdef DO_DEBUG_PRINT +#define dprintf printf +#define dputs puts +#else +#define dprintf +#define dputs +#endif + +#define Z80_VINT_DURATION 128 + +void z80_next_int_pulse(z80_context * z_context) +{ + genesis_context * gen = z_context->system; + z_context->int_pulse_start = vdp_next_vint_z80(gen->vdp); + z_context->int_pulse_end = z_context->int_pulse_start + Z80_VINT_DURATION * MCLKS_PER_Z80; + } + +void sync_z80(z80_context * z_context, uint32_t mclks) +{ +#ifndef NO_Z80 + if (z80_enabled) { + z80_run(z_context, mclks); + } else +#endif + { + z_context->current_cycle = mclks; + } +} + +void sync_sound(genesis_context * gen, uint32_t target) +{ + //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); + while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { + uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; + //printf("Running PSG to cycle %d\n", cur_target); + psg_run(gen->psg, cur_target); + //printf("Running YM-2612 to cycle %d\n", cur_target); + ym_run(gen->ym, cur_target); + } + psg_run(gen->psg, target); + ym_run(gen->ym, target); + + //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); +} + +uint32_t last_frame_num; + +//My refresh emulation isn't currently good enough and causes more problems than it solves +#ifdef REFRESH_EMULATION +#define REFRESH_INTERVAL 128 +#define REFRESH_DELAY 2 +uint32_t last_sync_cycle; +uint32_t refresh_counter; +#endif + +m68k_context * sync_components(m68k_context * context, uint32_t address) +{ + genesis_context * gen = context->system; + vdp_context * v_context = gen->vdp; + z80_context * z_context = gen->z80; +#ifdef REFRESH_EMULATION + //lame estimation of refresh cycle delay + if (!gen->bus_busy) { + refresh_counter += context->current_cycle - last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + } +#endif + + uint32_t mclks = context->current_cycle; + sync_z80(z_context, mclks); + sync_sound(gen, mclks); + vdp_run_context(v_context, mclks); + if (v_context->frame != last_frame_num) { + //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); + last_frame_num = v_context->frame; + + if(exit_after){ + --exit_after; + if (!exit_after) { + exit(0); + } + } + + vdp_adjust_cycles(v_context, mclks); + io_adjust_cycles(gen->ports, context->current_cycle, mclks); + io_adjust_cycles(gen->ports+1, context->current_cycle, mclks); + io_adjust_cycles(gen->ports+2, context->current_cycle, mclks); + context->current_cycle -= mclks; + z80_adjust_cycles(z_context, mclks); + gen->ym->current_cycle -= mclks; + gen->psg->cycles -= mclks; + if (gen->ym->write_cycle != CYCLE_NEVER) { + gen->ym->write_cycle = gen->ym->write_cycle >= mclks ? gen->ym->write_cycle - mclks : 0; + } + } + gen->frame_end = vdp_cycles_to_frame_end(v_context); + context->sync_cycle = gen->frame_end; + //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); + if (context->int_ack) { + //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); + vdp_int_ack(v_context); + context->int_ack = 0; + } + if (!address && (break_on_sync || gen->save_state)) { + context->sync_cycle = context->current_cycle + 1; + } + adjust_int_cycle(context, v_context); + if (address) { + if (break_on_sync) { + break_on_sync = 0; + debugger(context, address); + } + if (gen->save_state && (z_context->pc || (!z_context->reset && !z_context->busreq))) { + uint8_t slot = gen->save_state - 1; + gen->save_state = 0; + //advance Z80 core to the start of an instruction + while (!z_context->pc) + { + sync_z80(z_context, z_context->current_cycle + MCLKS_PER_Z80); + } + char *save_path; + if (slot == QUICK_SAVE_SLOT) { + save_path = save_state_path; + } else { + char slotname[] = "slot_0.gst"; + slotname[5] = '0' + slot; + char const *parts[] = {gen->save_dir, PATH_SEP, slotname}; + save_path = alloc_concat_m(3, parts); + } + save_gst(gen, save_path, address); + printf("Saved state to %s\n", save_path); + if (slot != QUICK_SAVE_SLOT) { + free(save_path); + } + } else if(gen->save_state) { + context->sync_cycle = context->current_cycle + 1; + } + } +#ifdef REFRESH_EMULATION + last_sync_cycle = context->current_cycle; +#endif + return context; +} + +m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) +{ + if (vdp_port & 0x2700E0) { + fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port); + } + vdp_port &= 0x1F; + //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); + sync_components(context, 0); + genesis_context * gen = context->system; + vdp_context *v_context = gen->vdp; + if (vdp_port < 0x10) { + int blocked; + uint32_t before_cycle = v_context->cycles; + if (vdp_port < 4) { + + while (vdp_data_port_write(v_context, value) < 0) { + while(v_context->flags & FLAG_DMA_RUN) { + vdp_run_dma_done(v_context, gen->frame_end); + if (v_context->cycles >= gen->frame_end) { + uint32_t cycle_diff = v_context->cycles - context->current_cycle; + uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; + if (m68k_cycle_diff < cycle_diff) { + m68k_cycle_diff += MCLKS_PER_68K; + } + context->current_cycle += m68k_cycle_diff; + gen->bus_busy = 1; + sync_components(context, 0); + gen->bus_busy = 0; + } + } + //context->current_cycle = v_context->cycles; + } + } else if(vdp_port < 8) { + blocked = vdp_control_port_write(v_context, value); + if (blocked) { + while (blocked) { + while(v_context->flags & FLAG_DMA_RUN) { + vdp_run_dma_done(v_context, gen->frame_end); + if (v_context->cycles >= gen->frame_end) { + uint32_t cycle_diff = v_context->cycles - context->current_cycle; + uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; + if (m68k_cycle_diff < cycle_diff) { + m68k_cycle_diff += MCLKS_PER_68K; + } + context->current_cycle += m68k_cycle_diff; + gen->bus_busy = 1; + sync_components(context, 0); + gen->bus_busy = 0; + } + if (!(v_context->flags & FLAG_DMA_RUN)) { + //two more slots of delay are needed to kill sufficient sprite capacity in Overdrive + //TODO: Measure exact value with logic analyzer + vdp_run_context(v_context, v_context->cycles + 1); + vdp_run_context(v_context, v_context->cycles + 1); + } + } + + if (blocked < 0) { + blocked = vdp_control_port_write(v_context, value); + } else { + blocked = 0; + } + } + } else { + context->sync_cycle = gen->frame_end = vdp_cycles_to_frame_end(v_context); + //printf("Set sync cycle to: %d @ %d, vcounter: %d, hslot: %d\n", context->sync_cycle, context->current_cycle, v_context->vcounter, v_context->hslot); + adjust_int_cycle(context, v_context); + } + } else { + fatal_error("Illegal write to HV Counter port %X\n", vdp_port); + } + if (v_context->cycles != before_cycle) { + //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); + uint32_t cycle_diff = v_context->cycles - context->current_cycle; + uint32_t m68k_cycle_diff = (cycle_diff / MCLKS_PER_68K) * MCLKS_PER_68K; + if (m68k_cycle_diff < cycle_diff) { + m68k_cycle_diff += MCLKS_PER_68K; + } + context->current_cycle += m68k_cycle_diff; +#ifdef REFRESH_EMULATION + last_sync_cycle = context->current_cycle; +#endif + //Lock the Z80 out of the bus until the VDP access is complete + gen->bus_busy = 1; + sync_z80(gen->z80, v_context->cycles); + gen->bus_busy = 0; + } + } else if (vdp_port < 0x18) { + psg_write(gen->psg, value); + } else { + //TODO: Implement undocumented test register(s) + } + return context; +} + +m68k_context * vdp_port_write_b(uint32_t vdp_port, m68k_context * context, uint8_t value) +{ + return vdp_port_write(vdp_port, context, vdp_port < 0x10 ? value | value << 8 : ((vdp_port & 1) ? value : 0)); +} + +void * z80_vdp_port_write(uint32_t vdp_port, void * vcontext, uint8_t value) +{ + z80_context * context = vcontext; + genesis_context * gen = context->system; + vdp_port &= 0xFF; + if (vdp_port & 0xE0) { + fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port); + } + if (vdp_port < 0x10) { + //These probably won't currently interact well with the 68K accessing the VDP + vdp_run_context(gen->vdp, context->current_cycle); + if (vdp_port < 4) { + vdp_data_port_write(gen->vdp, value << 8 | value); + } else if (vdp_port < 8) { + vdp_control_port_write(gen->vdp, value << 8 | value); + } else { + fatal_error("Illegal write to HV Counter port %X\n", vdp_port); + } + } else if (vdp_port < 0x18) { + sync_sound(gen, context->current_cycle); + psg_write(gen->psg, value); + } else { + vdp_test_port_write(gen->vdp, value); + } + return context; +} + +uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) +{ + if (vdp_port & 0x2700E0) { + fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port); + } + vdp_port &= 0x1F; + uint16_t value; + sync_components(context, 0); + genesis_context *gen = context->system; + vdp_context * v_context = gen->vdp; + uint32_t before_cycle = v_context->cycles; + if (vdp_port < 0x10) { + if (vdp_port < 4) { + value = vdp_data_port_read(v_context); + } else if(vdp_port < 8) { + value = vdp_control_port_read(v_context); + } else { + value = vdp_hv_counter_read(v_context); + //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); + } + } else if (vdp_port < 0x18){ + fatal_error("Illegal read from PSG port %X\n", vdp_port); + } else { + value = vdp_test_port_read(v_context); + } + if (v_context->cycles != before_cycle) { + //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); + context->current_cycle = v_context->cycles; +#ifdef REFRES_EMULATION + last_sync_cycle = context->current_cycle; +#endif + //Lock the Z80 out of the bus until the VDP access is complete + genesis_context *gen = context->system; + gen->bus_busy = 1; + sync_z80(gen->z80, v_context->cycles); + gen->bus_busy = 0; + } + return value; +} + +uint8_t vdp_port_read_b(uint32_t vdp_port, m68k_context * context) +{ + uint16_t value = vdp_port_read(vdp_port, context); + if (vdp_port & 1) { + return value; + } else { + return value >> 8; + } +} + +uint8_t z80_vdp_port_read(uint32_t vdp_port, void * vcontext) +{ + z80_context * context = vcontext; + if (vdp_port & 0xE0) { + fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port); + } + genesis_context * gen = context->system; + //VDP access goes over the 68K bus like a bank area access + //typical delay from bus arbitration + context->current_cycle += 3 * MCLKS_PER_Z80; + //TODO: add cycle for an access right after a previous one + //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high + // Needs a new logic analyzer capture to get the actual delay on the 68K side + gen->m68k->current_cycle += 8 * MCLKS_PER_68K; + + + vdp_port &= 0x1F; + uint16_t ret; + if (vdp_port < 0x10) { + //These probably won't currently interact well with the 68K accessing the VDP + vdp_run_context(gen->vdp, context->current_cycle); + if (vdp_port < 4) { + ret = vdp_data_port_read(gen->vdp); + } else if (vdp_port < 8) { + ret = vdp_control_port_read(gen->vdp); + } else { + fatal_error("Illegal write to HV Counter port %X\n", vdp_port); + } + } else { + //TODO: Figure out the correct value today + ret = 0xFFFF; + } + return vdp_port & 1 ? ret : ret >> 8; +} + +uint32_t zram_counter = 0; + +m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) +{ + genesis_context * gen = context->system; + if (location < 0x10000) { + //Access to Z80 memory incurs a one 68K cycle wait state + context->current_cycle += MCLKS_PER_68K; + if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { + location &= 0x7FFF; + if (location < 0x4000) { + z80_ram[location & 0x1FFF] = value; +#ifndef NO_Z80 + z80_handle_code_write(location & 0x1FFF, gen->z80); +#endif + } else if (location < 0x6000) { + sync_sound(gen, context->current_cycle); + if (location & 1) { + ym_data_write(gen->ym, value); + } else if(location & 2) { + ym_address_write_part2(gen->ym, value); + } else { + ym_address_write_part1(gen->ym, value); + } + } else if (location == 0x6000) { + gen->z80->bank_reg = (gen->z80->bank_reg >> 1 | value << 8) & 0x1FF; + if (gen->z80->bank_reg < 0x80) { + gen->z80->mem_pointers[1] = (gen->z80->bank_reg << 15) + ((char *)gen->z80->mem_pointers[2]); + } else { + gen->z80->mem_pointers[1] = NULL; + } + } else { + fatal_error("68K write to unhandled Z80 address %X\n", location); + } + } + } else { + location &= 0x1FFF; + if (location < 0x100) { + switch(location/2) + { + case 0x1: + io_data_write(gen->ports, value, context->current_cycle); + break; + case 0x2: + io_data_write(gen->ports+1, value, context->current_cycle); + break; + case 0x3: + io_data_write(gen->ports+2, value, context->current_cycle); + break; + case 0x4: + gen->ports[0].control = value; + break; + case 0x5: + gen->ports[1].control = value; + break; + case 0x6: + gen->ports[2].control = value; + break; + } + } else { + if (location == 0x1100) { + if (value & 1) { + dputs("bus requesting Z80"); + if (z80_enabled) { + z80_assert_busreq(gen->z80, context->current_cycle); + } else { + gen->z80->busack = 1; + } + } else { + if (gen->z80->busreq) { + dputs("releasing z80 bus"); + #ifdef DO_DEBUG_PRINT + char fname[20]; + sprintf(fname, "zram-%d", zram_counter++); + FILE * f = fopen(fname, "wb"); + fwrite(z80_ram, 1, sizeof(z80_ram), f); + fclose(f); + #endif + } + if (z80_enabled) { + z80_clear_busreq(gen->z80, context->current_cycle); + } else { + gen->z80->busack = 0; + } + } + } else if (location == 0x1200) { + sync_z80(gen->z80, context->current_cycle); + if (value & 1) { + if (z80_enabled) { + z80_clear_reset(gen->z80, context->current_cycle); + } else { + gen->z80->reset = 0; + } + } else { + if (z80_enabled) { + z80_assert_reset(gen->z80, context->current_cycle); + } else { + gen->z80->reset = 1; + } + } + } + } + } + return context; +} + +m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) +{ + if (location < 0x10000 || (location & 0x1FFF) >= 0x100) { + return io_write(location, context, value >> 8); + } else { + return io_write(location, context, value); + } +} + +#define FOREIGN 0x80 +#define HZ50 0x40 +#define USA FOREIGN +#define JAP 0x00 +#define EUR (HZ50|FOREIGN) +#define NO_DISK 0x20 + +uint8_t io_read(uint32_t location, m68k_context * context) +{ + uint8_t value; + genesis_context *gen = context->system; + if (location < 0x10000) { + //Access to Z80 memory incurs a one 68K cycle wait state + context->current_cycle += MCLKS_PER_68K; + if (!z80_enabled || z80_get_busack(gen->z80, context->current_cycle)) { + location &= 0x7FFF; + if (location < 0x4000) { + value = z80_ram[location & 0x1FFF]; + } else if (location < 0x6000) { + sync_sound(gen, context->current_cycle); + value = ym_read_status(gen->ym); + } else { + value = 0xFF; + } + } else { + value = 0xFF; + } + } else { + location &= 0x1FFF; + if (location < 0x100) { + switch(location/2) + { + case 0x0: + //version bits should be 0 for now since we're not emulating TMSS + value = gen->version_reg; + break; + case 0x1: + value = io_data_read(gen->ports, context->current_cycle); + break; + case 0x2: + value = io_data_read(gen->ports+1, context->current_cycle); + break; + case 0x3: + value = io_data_read(gen->ports+2, context->current_cycle); + break; + case 0x4: + value = gen->ports[0].control; + break; + case 0x5: + value = gen->ports[1].control; + break; + case 0x6: + value = gen->ports[2].control; + break; + default: + value = 0xFF; + } + } else { + if (location == 0x1100) { + value = z80_enabled ? !z80_get_busack(gen->z80, context->current_cycle) : !gen->z80->busack; + value |= (get_open_bus_value() >> 8) & 0xFE; + dprintf("Byte read of BUSREQ returned %d @ %d (reset: %d)\n", value, context->current_cycle, gen->z80->reset); + } else if (location == 0x1200) { + value = !gen->z80->reset; + } else { + value = 0xFF; + printf("Byte read of unknown IO location: %X\n", location); + } + } + } + return value; +} + +uint16_t io_read_w(uint32_t location, m68k_context * context) +{ + uint16_t value = io_read(location, context); + if (location < 0x10000 || (location & 0x1FFF) < 0x100) { + value = value | (value << 8); + } else { + value <<= 8; + value |= get_open_bus_value() & 0xFF; + } + return value; +} + +void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value) +{ + z80_context * context = vcontext; + genesis_context * gen = context->system; + sync_sound(gen, context->current_cycle); + if (location & 1) { + ym_data_write(gen->ym, value); + } else if (location & 2) { + ym_address_write_part2(gen->ym, value); + } else { + ym_address_write_part1(gen->ym, value); + } + return context; +} + +uint8_t z80_read_ym(uint32_t location, void * vcontext) +{ + z80_context * context = vcontext; + genesis_context * gen = context->system; + sync_sound(gen, context->current_cycle); + return ym_read_status(gen->ym); +} + +uint8_t z80_read_bank(uint32_t location, void * vcontext) +{ + z80_context * context = vcontext; + genesis_context *gen = context->system; + if (gen->bus_busy) { + context->current_cycle = context->sync_cycle; + } + //typical delay from bus arbitration + context->current_cycle += 3 * MCLKS_PER_Z80; + //TODO: add cycle for an access right after a previous one + //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high + // Needs a new logic analyzer capture to get the actual delay on the 68K side + gen->m68k->current_cycle += 8 * MCLKS_PER_68K; + + location &= 0x7FFF; + if (context->mem_pointers[1]) { + return context->mem_pointers[1][location ^ 1]; + } + uint32_t address = context->bank_reg << 15 | location; + if (address >= 0xC00000 && address < 0xE00000) { + return z80_vdp_port_read(location & 0xFF, context); + } else { + fprintf(stderr, "Unhandled read by Z80 from address %X through banked memory area (%X)\n", address, context->bank_reg << 15); + } + return 0; +} + +void *z80_write_bank(uint32_t location, void * vcontext, uint8_t value) +{ + z80_context * context = vcontext; + genesis_context *gen = context->system; + if (gen->bus_busy) { + context->current_cycle = context->sync_cycle; + } + //typical delay from bus arbitration + context->current_cycle += 3 * MCLKS_PER_Z80; + //TODO: add cycle for an access right after a previous one + //TODO: Below cycle time is an estimate based on the time between 68K !BG goes low and Z80 !MREQ goes high + // Needs a new logic analyzer capture to get the actual delay on the 68K side + gen->m68k->current_cycle += 8 * MCLKS_PER_68K; + + location &= 0x7FFF; + uint32_t address = context->bank_reg << 15 | location; + if (address >= 0xE00000) { + address &= 0xFFFF; + ((uint8_t *)ram)[address ^ 1] = value; + } else if (address >= 0xC00000) { + z80_vdp_port_write(location & 0xFF, context, value); + } else { + fprintf(stderr, "Unhandled write by Z80 to address %X through banked memory area\n", address); + } + return context; +} + +void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value) +{ + z80_context * context = vcontext; + + context->bank_reg = (context->bank_reg >> 1 | value << 8) & 0x1FF; + if (context->bank_reg < 0x100) { + genesis_context *gen = context->system; + context->mem_pointers[1] = get_native_pointer(context->bank_reg << 15, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen); + } else { + context->mem_pointers[1] = NULL; + } + + return context; +} + +void set_speed_percent(genesis_context * context, uint32_t percent) +{ + uint32_t old_clock = context->master_clock; + context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; + while (context->ym->current_cycle != context->psg->cycles) { + sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); +} + ym_adjust_master_clock(context->ym, context->master_clock); + psg_adjust_master_clock(context->psg, context->master_clock); +} + +void set_region(genesis_context *gen, rom_info *info, uint8_t region) +{ + if (!region) { + char * def_region = tern_find_ptr(config, "default_region"); + if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) { + region = translate_region_char(toupper(*def_region)); + } else { + region = info->regions; + } + } + if (region & REGION_E) { + gen->version_reg = NO_DISK | EUR; + } else if (region & REGION_J) { + gen->version_reg = NO_DISK | JAP; + } else { + gen->version_reg = NO_DISK | USA; + } + + if (region & HZ50) { + gen->normal_clock = MCLKS_PAL; + } else { + gen->normal_clock = MCLKS_NTSC; + } + gen->master_clock = gen->normal_clock; +} + +genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t ym_opts, uint8_t force_region) +{ + static memmap_chunk z80_map[] = { + { 0x0000, 0x4000, 0x1FFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL, NULL, NULL, NULL, NULL }, + { 0x8000, 0x10000, 0x7FFF, 0, 0, 0, NULL, NULL, NULL, z80_read_bank, z80_write_bank}, + { 0x4000, 0x6000, 0x0003, 0, 0, 0, NULL, NULL, NULL, z80_read_ym, z80_write_ym}, + { 0x6000, 0x6100, 0xFFFF, 0, 0, 0, NULL, NULL, NULL, NULL, z80_write_bank_reg}, + { 0x7F00, 0x8000, 0x00FF, 0, 0, 0, NULL, NULL, NULL, z80_vdp_port_read, z80_vdp_port_write} + }; + genesis_context *gen = calloc(1, sizeof(genesis_context)); + set_region(gen, rom, force_region); + + gen->vdp = malloc(sizeof(vdp_context)); + init_vdp_context(gen->vdp, gen->version_reg & 0x40); + gen->frame_end = vdp_cycles_to_frame_end(gen->vdp); + char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval; + gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL; + + char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval; + uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF; + + gen->ym = malloc(sizeof(ym2612_context)); + ym_init(gen->ym, render_sample_rate(), gen->master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_opts, lowpass_cutoff); + + gen->psg = malloc(sizeof(psg_context)); + psg_init(gen->psg, render_sample_rate(), gen->master_clock, MCLKS_PER_PSG, render_audio_buffer(), lowpass_cutoff); + + gen->z80 = calloc(1, sizeof(z80_context)); + z80_map[0].buffer = gen->zram = calloc(1, Z80_RAM_BYTES); +#ifndef NO_Z80 + z80_options *z_opts = malloc(sizeof(z80_options)); + init_z80_opts(z_opts, z80_map, 5, NULL, 0, MCLKS_PER_Z80); + init_z80_context(gen->z80, z_opts); + z80_assert_reset(gen->z80, 0); +#endif + + gen->z80->system = gen; + gen->z80->mem_pointers[0] = gen->zram; + gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)cart; + + gen->cart = main_rom; + gen->lock_on = lock_on; + gen->work_ram = calloc(2, RAM_WORDS); + gen->zram = z80_ram; + setup_io_devices(config, rom, gen); + + gen->save_type = rom->save_type; + gen->save_type = rom->save_type; + if (gen->save_type != SAVE_NONE) { + gen->save_ram_mask = rom->save_mask; + gen->save_size = rom->save_size; + gen->save_storage = rom->save_buffer; + gen->eeprom_map = rom->eeprom_map; + gen->num_eeprom = rom->num_eeprom; + if (gen->save_type == SAVE_I2C) { + eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); + } + } else { + gen->save_storage = NULL; + } + + for (int i = 0; i < rom->map_chunks; i++) + { + if (rom->map[i].flags & MMAP_PTR_IDX) { + gen->m68k->mem_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; + } + if (rom->map[i].start == 0xE00000) { + rom->map[i].buffer = gen->work_ram; + } + } + + m68k_options *opts = malloc(sizeof(m68k_options)); + init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K); + //TODO: make this configurable + opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; + gen->m68k = init_68k_context(opts, NULL); + gen->m68k->system = gen; + + return gen; +} + + + +void free_genesis(genesis_context *gen) +{ + vdp_free(gen->vdp); + m68k_options_free(gen->m68k->options); + free(gen->m68k); + free(gen->work_ram); + z80_options_free(gen->z80->options); + free(gen->z80); + free(gen->zram); + ym_free(gen->ym); + psg_free(gen->psg); + free(gen->save_storage); + free(gen->save_dir); + free(gen->lock_on); +} + +void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger) +{ + + if (statefile) { + uint32_t pc = load_gst(gen, statefile); + if (!pc) { + fatal_error("Failed to load save state %s\n", statefile); + } + printf("Loaded %s\n", statefile); + if (debugger) { + insert_breakpoint(gen->m68k, pc, debugger); + } + adjust_int_cycle(gen->m68k, gen->vdp); + start_68k_context(gen->m68k, pc); + } else { + if (debugger) { + uint32_t address = cart[2] << 16 | cart[3]; + insert_breakpoint(gen->m68k, address, debugger); + } + m68k_reset(gen->m68k); + } +} + +genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out) +{ + static memmap_chunk base_map[] = { + {0xE00000, 0x1000000, 0xFFFF, 0, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, NULL, + NULL, NULL, NULL, NULL}, + {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, 0, NULL, + (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, + (read_8_fun)vdp_port_read_b, (write_8_fun)vdp_port_write_b}, + {0xA00000, 0xA12000, 0x1FFFF, 0, 0, 0, NULL, + (read_16_fun)io_read_w, (write_16_fun)io_write_w, + (read_8_fun)io_read, (write_8_fun)io_write} + }; + static tern_node *rom_db; + if (!rom_db) { + rom_db = load_rom_db(); + } + *info_out = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0])); +#ifndef BIG_ENDIAN + byteswap_rom(rom, rom_size); + if (lock_on) { + byteswap_rom(lock_on, lock_on_size); + } +#endif + char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval; + if (!m68k_divider) { + m68k_divider = "7"; + } + MCLKS_PER_68K = atoi(m68k_divider); + if (!MCLKS_PER_68K) { + MCLKS_PER_68K = 7; + } + return alloc_init_genesis(info_out, rom, lock_on, ym_opts, force_region); +} diff -r c15896605bf2 -r 22e87b739ad6 genesis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genesis.h Fri Dec 09 09:48:48 2016 -0800 @@ -0,0 +1,74 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#ifndef GENESIS_H_ +#define GENESIS_H_ + +#include +#include "m68k_core.h" +#include "z80_to_x86.h" +#include "ym2612.h" +#include "vdp.h" +#include "psg.h" +#include "io.h" +#include "romdb.h" +#include "arena.h" + +typedef struct genesis_context genesis_context; + +struct genesis_context { + m68k_context *m68k; + z80_context *z80; + vdp_context *vdp; + ym2612_context *ym; + psg_context *psg; + genesis_context *next_context; + uint16_t *cart; + uint16_t *lock_on; + uint16_t *work_ram; + uint8_t *zram; + void *extra; + arena *arena; + char *next_rom; + char *save_dir; + uint8_t *save_storage; + eeprom_map *eeprom_map; + uint32_t num_eeprom; + uint32_t save_size; + uint32_t save_ram_mask; + uint32_t master_clock; //Current master clock value + uint32_t normal_clock; //Normal master clock (used to restore master clock after turbo mode) + uint32_t frame_end; + uint32_t max_cycles; + uint8_t bank_regs[8]; + uint16_t mapper_start_index; + uint8_t save_type; + io_port ports[3]; + uint8_t version_reg; + uint8_t bus_busy; + uint8_t should_exit; + uint8_t save_state; + uint8_t mouse_mode; + uint8_t mouse_captured; + eeprom_state eeprom; +}; + +extern genesis_context * genesis; +extern int break_on_sync; + +#define RAM_WORDS 32 * 1024 +#define Z80_RAM_BYTES 8 * 1024 + +uint16_t read_dma_value(uint32_t address); +uint16_t get_open_bus_value(); +m68k_context * sync_components(m68k_context *context, uint32_t address); +m68k_context * debugger(m68k_context * context, uint32_t address); +void set_speed_percent(genesis_context * context, uint32_t percent); +genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, rom_info *info_out); +void start_genesis(genesis_context *gen, char *statefile, uint8_t *debugger); +void free_genesis(genesis_context *gen); + +#endif //GENESIS_H_ + diff -r c15896605bf2 -r 22e87b739ad6 gst.c --- a/gst.c Mon Nov 28 22:45:46 2016 -0800 +++ b/gst.c Fri Dec 09 09:48:48 2016 -0800 @@ -3,8 +3,10 @@ This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ +#include "genesis.h" #include "gst.h" #include +#include #define GST_68K_REGS 0x80 #define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) diff -r c15896605bf2 -r 22e87b739ad6 io.c --- a/io.c Mon Nov 28 22:45:46 2016 -0800 +++ b/io.c Fri Dec 09 09:48:48 2016 -0800 @@ -17,6 +17,7 @@ #include "io.h" #include "blastem.h" +#include "genesis.h" #include "render.h" #include "util.h" diff -r c15896605bf2 -r 22e87b739ad6 menu.c --- a/menu.c Mon Nov 28 22:45:46 2016 -0800 +++ b/menu.c Fri Dec 09 09:48:48 2016 -0800 @@ -3,7 +3,7 @@ #include #include #include -#include "blastem.h" +#include "genesis.h" #include "menu.h" #include "backend.h" #include "util.h" diff -r c15896605bf2 -r 22e87b739ad6 render.h --- a/render.h Mon Nov 28 22:45:46 2016 -0800 +++ b/render.h Fri Dec 09 09:48:48 2016 -0800 @@ -60,7 +60,7 @@ uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); uint32_t *render_get_framebuffer(uint8_t which, int *pitch); void render_framebuffer_updated(uint8_t which, int width); -void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen); +void render_init(int width, int height, char * title, uint8_t fullscreen); void render_update_caption(char *title); void render_wait_quit(vdp_context * context); void render_wait_psg(psg_context * context); diff -r c15896605bf2 -r 22e87b739ad6 render_sdl.c --- a/render_sdl.c Mon Nov 28 22:45:46 2016 -0800 +++ b/render_sdl.c Fri Dec 09 09:48:48 2016 -0800 @@ -9,6 +9,7 @@ #include #include "render.h" #include "blastem.h" +#include "genesis.h" #include "io.h" #include "util.h" @@ -32,9 +33,6 @@ static uint32_t last_frame = 0; -static uint32_t min_delay; -static uint32_t frame_delay = 1000/60; - static int16_t * current_psg = NULL; static int16_t * current_ym = NULL; @@ -245,7 +243,7 @@ } } -void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) +void render_init(int width, int height, char * title, uint8_t fullscreen) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { fatal_error("Unable to init SDL: %s\n", SDL_GetError()); @@ -362,21 +360,6 @@ scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); caption = title; - min_delay = 0; - for (int i = 0; i < 100; i++) { - uint32_t start = SDL_GetTicks(); - SDL_Delay(1); - uint32_t delay = SDL_GetTicks()-start; - if (delay > min_delay) { - min_delay = delay; - } - } - if (!min_delay) { - min_delay = 1; - } - printf("minimum delay: %d\n", min_delay); - - frame_delay = 1000/fps; audio_mutex = SDL_CreateMutex(); psg_cond = SDL_CreateCond(); diff -r c15896605bf2 -r 22e87b739ad6 romdb.c --- a/romdb.c Mon Nov 28 22:45:46 2016 -0800 +++ b/romdb.c Fri Dec 09 09:48:48 2016 -0800 @@ -3,7 +3,7 @@ #include "config.h" #include "romdb.h" #include "util.h" -#include "blastem.h" +#include "genesis.h" #include "menu.h" #define DOM_TITLE_START 0x120 @@ -287,15 +287,15 @@ } } else { //Used for games that only use the mapper for SRAM - context->mem_pointers[gen->mapper_start_index] = cart + 0x200000/2; + context->mem_pointers[gen->mapper_start_index] = gen->cart + 0x200000/2; //For games that need more than 4MB for (int i = 1; i < 8; i++) { - context->mem_pointers[gen->mapper_start_index + i] = cart + 0x40000*gen->bank_regs[i]; + context->mem_pointers[gen->mapper_start_index + i] = gen->cart + 0x40000*gen->bank_regs[i]; } } } else { - context->mem_pointers[gen->mapper_start_index + address] = cart + 0x40000*value; + context->mem_pointers[gen->mapper_start_index + address] = gen->cart + 0x40000*value; } return context; } @@ -543,7 +543,7 @@ info->map[1].read_8 = (read_8_fun)read_sram_b; info->map[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area info->map[1].write_8 = (write_8_fun)write_sram_area_b; - info->map[1].buffer = cart + 0x200000 / sizeof(uint16_t); + info->map[1].buffer = rom + 0x200000 / sizeof(uint16_t); memmap_chunk *last = info->map + info->map_chunks - 1; memset(last, 0, sizeof(memmap_chunk)); diff -r c15896605bf2 -r 22e87b739ad6 stateview.c --- a/stateview.c Mon Nov 28 22:45:46 2016 -0800 +++ b/stateview.c Fri Dec 09 09:48:48 2016 -0800 @@ -8,7 +8,8 @@ #include "vdp.h" #include "render.h" #include "util.h" -#include "blastem.h" +#include "genesis.h" +#include "config.h" uint16_t read_dma_value(uint32_t address) @@ -109,7 +110,7 @@ height = height < 240 ? (width/320) * 240 : height; vdp_context context; - render_init(width, height, "GST State Viewer", 60, 0); + render_init(width, height, "GST State Viewer", 0); init_vdp_context(&context, 0); vdp_load_gst(&context, state_file); vdp_run_to_vblank(&context); diff -r c15896605bf2 -r 22e87b739ad6 util.c --- a/util.c Mon Nov 28 22:45:46 2016 -0800 +++ b/util.c Fri Dec 09 09:48:48 2016 -0800 @@ -57,6 +57,15 @@ return ret; } +void byteswap_rom(int filesize, uint16_t *cart) +{ + for(uint16_t *cur = cart; cur - cart < filesize/2; ++cur) + { + *cur = (*cur >> 8) | (*cur << 8); + } +} + + long file_size(FILE * f) { fseek(f, 0, SEEK_END); diff -r c15896605bf2 -r 22e87b739ad6 util.h --- a/util.h Mon Nov 28 22:45:46 2016 -0800 +++ b/util.h Fri Dec 09 09:48:48 2016 -0800 @@ -21,6 +21,8 @@ char * alloc_concat(char const * first, char const * second); //Allocates a new string containing the concatenation of the strings pointed to by parts char * alloc_concat_m(int num_parts, char const ** parts); +//Byteswaps a ROM image in memory +void byteswap_rom(int filesize, uint16_t *cart); //Returns the size of a file using fseek and ftell long file_size(FILE * f); //Strips whitespace and non-printable characters from the beginning and end of a string diff -r c15896605bf2 -r 22e87b739ad6 vdp.c --- a/vdp.c Mon Nov 28 22:45:46 2016 -0800 +++ b/vdp.c Fri Dec 09 09:48:48 2016 -0800 @@ -5,6 +5,7 @@ */ #include "vdp.h" #include "blastem.h" +#include "genesis.h" #include #include #include "render.h" diff -r c15896605bf2 -r 22e87b739ad6 vgmplay.c --- a/vgmplay.c Mon Nov 28 22:45:46 2016 -0800 +++ b/vgmplay.c Fri Dec 09 09:48:48 2016 -0800 @@ -89,7 +89,7 @@ uint32_t fps = 60; config = load_config(argv[0]); - render_init(320, 240, "vgm play", 60, 0); + render_init(320, 240, "vgm play", 0); uint32_t opts = 0; if (argc >= 3 && !strcmp(argv[2], "-y")) {