# HG changeset patch # User Michael Pavone # Date 1708587739 28800 # Node ID cb62730d5c99c9c8cce438031f9505d8571983a1 # Parent 09c9d2c6bac04cdf4cd774cf92b7922d5c70b28c Initial work on Copera emulation diff -r 09c9d2c6bac0 -r cb62730d5c99 blastem.c --- a/blastem.c Wed Feb 21 20:34:52 2024 -0800 +++ b/blastem.c Wed Feb 21 23:42:19 2024 -0800 @@ -615,6 +615,8 @@ stype = force_stype = SYSTEM_GENESIS; } else if (!strcmp("pico", argv[i])) { stype = force_stype = SYSTEM_PICO; + } else if (!strcmp("copera", argv[i])) { + stype = force_stype = SYSTEM_COPERA; } else if (!strcmp("jag", argv[i])) { stype = force_stype = SYSTEM_JAGUAR; } else if (!strcmp("media", argv[i])) { diff -r 09c9d2c6bac0 -r cb62730d5c99 debug.c --- a/debug.c Wed Feb 21 20:34:52 2024 -0800 +++ b/debug.c Wed Feb 21 23:42:19 2024 -0800 @@ -4830,15 +4830,24 @@ { case SYSTEM_GENESIS: case SYSTEM_SEGACD: + case SYSTEM_PICO: + case SYSTEM_COPERA: //check if this is the main CPU if (context->system == current_system) { genesis_context *gen = context->system; - root->other_roots = tern_insert_ptr(root->other_roots, "z80", find_z80_root(gen->z80)); + if (current_system->type == SYSTEM_GENESIS || current_system->type == SYSTEM_SEGACD) { + root->other_roots = tern_insert_ptr(root->other_roots, "z80", find_z80_root(gen->z80)); + root->other_roots = tern_insert_ptr(root->other_roots, "io", find_io_root(&gen->io)); + } root->other_roots = tern_insert_ptr(root->other_roots, "vdp", find_vdp_root(gen->vdp)); - root->other_roots = tern_insert_ptr(root->other_roots, "ym", find_ym2612_root(gen->ym)); root->other_roots = tern_insert_ptr(root->other_roots, "psg", find_psg_root(gen->psg)); - root->other_roots = tern_insert_ptr(root->other_roots, "io", find_io_root(&gen->io)); - add_commands(root, genesis_commands, NUM_GENESIS); + uint32_t num_commands = NUM_GENESIS; + if (current_system->type == SYSTEM_PICO || current_system->type == SYSTEM_COPERA) { + num_commands -= 3; + } else { + root->other_roots = tern_insert_ptr(root->other_roots, "ym", find_ym2612_root(gen->ym)); + } + add_commands(root, genesis_commands, num_commands); var = calloc(1, sizeof(debug_var)); var->get = debug_frame_get; var->ptr = gen->vdp; diff -r 09c9d2c6bac0 -r cb62730d5c99 genesis.c --- a/genesis.c Wed Feb 21 20:34:52 2024 -0800 +++ b/genesis.c Wed Feb 21 23:42:19 2024 -0800 @@ -70,7 +70,7 @@ vdp_serialize(gen->vdp, buf); end_section(buf); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { start_section(buf, SECTION_YM2612); ym_serialize(gen->ym, buf); end_section(buf); @@ -235,7 +235,7 @@ register_section_handler(buf, (section_handler){.fun = zram_deserialize, .data = gen}, SECTION_SOUND_RAM); register_section_handler(buf, (section_handler){.fun = tmss_deserialize, .data = gen}, SECTION_TMSS); } - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { register_section_handler(buf, (section_handler){.fun = ym_deserialize, .data = gen->ym}, SECTION_YM2612); } if (gen->expansion) { @@ -906,7 +906,7 @@ static m68k_context *int_ack(m68k_context *context) { genesis_context * gen = context->system; - if (gen->header.type != SYSTEM_PICO || context->int_num > 4 || context->int_num < 3) { + if ((gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) || context->int_num > 4 || context->int_num < 3) { vdp_context * v_context = gen->vdp; //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_run_context(v_context, context->current_cycle); @@ -940,7 +940,7 @@ //do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access gen_update_refresh_free_access(context); - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { sync_components_pico(context, 0); } else { sync_components(context, 0); @@ -963,7 +963,7 @@ } context->current_cycle += m68k_cycle_diff; gen->bus_busy = 1; - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { sync_components_pico(context, 0); } else { sync_components(context, 0); @@ -990,7 +990,7 @@ } context->current_cycle += m68k_cycle_diff; gen->bus_busy = 1; - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { sync_components_pico(context, 0); } else { sync_components(context, 0); @@ -1008,7 +1008,7 @@ } 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); - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { adjust_int_cycle_pico(context, v_context); } else { adjust_int_cycle(context, v_context); @@ -1097,7 +1097,7 @@ //do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access gen_update_refresh_free_access(context); - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { sync_components_pico(context, 0); } else { sync_components(context, 0); @@ -1513,6 +1513,13 @@ return gen->pico_pen_y; case 6: return gen->pico_page; + case 7: + //Copera titles seem to expect bit 0 to be 0 on a Copera and 1 on a Pico + if (gen->header.type == SYSTEM_PICO) { + return 1; + } else { + return 0; + } case 8: //printf("uPD7759 data read @ %u\n", m68k->current_cycle); sync_sound_pico(gen, m68k->current_cycle); @@ -1546,6 +1553,56 @@ return value | (value << 8); } +static uint16_t copera_io_read_w(uint32_t location, void *vcontext) +{ + printf("Unhandled Copera 16-bit read %X\n", location); + return 0xFFFF; +} + +static uint8_t copera_io_read(uint32_t location, void *vcontext) +{ + printf("Unhandled Copera 8-bit read %X\n", location); + return 0xFF; +} + +static void* copera_io_write_w(uint32_t location, void *vcontext, uint16_t value) +{ + printf("Unhandled Copera 16-bit write %X: %X\n", location, value); + return vcontext; +} + +static void* copera_io_write(uint32_t location, void *vcontext, uint8_t value) +{ + switch (location & 0xFF) + { + case 1: + case 5: + printf("Copera YMZ263 Address write - %X: %X\n", location, value); + break; + case 3: + case 7: + printf("Copera YMZ263 Channel #%d Data write - %X: %X\n", ((location & 4) >> 2) + 1, location, value); + break; + case 0x24: + case 0x34: + printf("Copera YMF263 Address Part #%d write - %X: %X\n", ((location >> 4) & 1) + 1, location, value); + break; + case 0x28: + printf("Copera YMF263 Data write - %X: %X\n", location, value); + break; + case 0x40: + //Bit 4 = SCI + //Bit 5 = DIN + //Bit 6 = A0 + //Possible Bit 0-3 are the same but for the other YM7128B + printf("Copera YM7128B Write - %X: %X\n", location, value); + break; + default: + printf("Unhandled Copera 8-bit write %X: %X\n", location, value); + } + return vcontext; +} + static void * z80_write_ym(uint32_t location, void * vcontext, uint8_t value) { z80_context * context = vcontext; @@ -1790,7 +1847,7 @@ } } uint8_t is_50hz = 0; - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { if (region & REGION_E) { is_50hz = 1; gen->version_reg = 0x20; @@ -1865,7 +1922,7 @@ z80_assert_reset(gen->z80, gen->m68k->current_cycle); z80_clear_busreq(gen->z80, gen->m68k->current_cycle); } - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { ym_reset(gen->ym); } //Is there any sort of VDP reset? @@ -1880,7 +1937,7 @@ if (gen->header.force_release || render_should_release_on_exit()) { bindings_release_capture(); vdp_release_framebuffer(gen->vdp); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { render_pause_source(gen->ym->audio); } render_pause_source(gen->psg->audio); @@ -1912,7 +1969,7 @@ insert_breakpoint(gen->m68k, pc, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter); #endif } - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { adjust_int_cycle_pico(gen->m68k, gen->vdp); } else { adjust_int_cycle(gen->m68k, gen->vdp); @@ -1938,14 +1995,14 @@ genesis_context *gen = (genesis_context *)system; if (gen->header.force_release || render_should_release_on_exit()) { gen->header.force_release = 0; - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { render_set_video_standard((gen->version_reg & 0x60) == 0x20 ? VID_PAL : VID_NTSC); } else { render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC); } bindings_reacquire_capture(); vdp_reacquire_framebuffer(gen->vdp); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { render_resume_source(gen->ym->audio); } render_resume_source(gen->psg->audio); @@ -2099,7 +2156,7 @@ free(gen->z80); free(gen->zram); } - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { pico_pcm_free(gen->adpcm); free(gen->adpcm); } else { @@ -2295,7 +2352,7 @@ char *config_gain; config_gain = tern_find_path(config, "audio\0psg_gain\0", TVAL_PTR).ptrval; render_audio_source_gaindb(gen->psg->audio, config_gain ? atof(config_gain) : 0.0f); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { config_gain = tern_find_path(config, "audio\0fm_gain\0", TVAL_PTR).ptrval; render_audio_source_gaindb(gen->ym->audio, config_gain ? atof(config_gain) : 0.0f); @@ -2320,7 +2377,7 @@ } set_audio_config(gen); //sample rate may have changed - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { ym_adjust_master_clock(gen->ym, gen->master_clock); } psg_adjust_master_clock(gen->psg, gen->master_clock); @@ -2336,7 +2393,7 @@ if (vgm) { printf("Started logging VGM to %s\n", filename); sync_sound(gen, vgm->last_cycle); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { ym_vgm_log(gen->ym, gen->normal_clock, vgm); } psg_vgm_log(gen->psg, gen->normal_clock, vgm); @@ -2351,7 +2408,7 @@ puts("Stopped VGM log"); genesis_context *gen = (genesis_context *)system; vgm_close(gen->ym->vgm); - if (gen->header.type != SYSTEM_PICO) { + if (gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) { gen->ym->vgm = NULL; } gen->psg->vgm = NULL; @@ -2367,7 +2424,7 @@ } else if (debug_view == DEBUG_OSCILLOSCOPE) { if (gen->psg->scope) { oscilloscope *scope = gen->psg->scope; - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { gen->adpcm->scope = NULL; } else { gen->ym->scope = NULL; @@ -2380,7 +2437,7 @@ scope_close(scope); } else { oscilloscope *scope = create_oscilloscope(); - if (gen->header.type == SYSTEM_PICO) { + if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { pico_pcm_enable_scope(gen->adpcm, scope, gen->normal_clock); } else { ym_enable_scope(gen->ym, scope, gen->normal_clock); @@ -2989,15 +3046,21 @@ {0xE00000, 0x1000000, 0xFFFF, .flags = MMAP_READ | MMAP_WRITE | MMAP_CODE}, {0xC00000, 0xE00000, 0x1FFFFF, .read_16 = (read_16_fun)vdp_port_read, .write_16 =(write_16_fun)vdp_port_write, .read_8 = (read_8_fun)vdp_port_read_b, .write_8 = (write_8_fun)vdp_port_write_b}, - {0x800000, 0x900000, 0xFFFFFF, .read_16 = pico_io_read_w, .write_16 = pico_io_write_w, - .read_8 = pico_io_read, .write_8 = pico_io_write} + {0x800000, 0x900000, 0xFFFFFF, .read_16 = pico_io_read_w, .write_16 = pico_io_write_w, + .read_8 = pico_io_read, .write_8 = pico_io_write}, + {0xB00000, 0xC00000, 0xFFFFFF, .read_16 = copera_io_read_w, .write_16 = copera_io_write_w, + .read_8 = copera_io_read, .write_8 = copera_io_write} }; const size_t pico_base_chunks = sizeof(pico_base_map)/sizeof(*pico_base_map); -genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region) +genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, system_type stype) { tern_node *rom_db = get_rom_db(); - rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, pico_base_map, pico_base_chunks); + uint32_t chunks = pico_base_chunks; + if (stype == SYSTEM_PICO) { + chunks--; + } + rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, pico_base_map, chunks); rom = info.rom; rom_size = info.rom_size; #ifndef BLASTEM_BIG_ENDIAN @@ -3040,7 +3103,7 @@ gen->header.start_vgm_log = start_vgm_log; gen->header.stop_vgm_log = stop_vgm_log; gen->header.toggle_debug_view = toggle_debug_view; - gen->header.type = SYSTEM_PICO; + gen->header.type = stype; gen->header.info = info; set_region(gen, &info, force_region); gen->vdp_unlocked = 1; diff -r 09c9d2c6bac0 -r cb62730d5c99 genesis.h --- a/genesis.h Wed Feb 21 20:34:52 2024 -0800 +++ b/genesis.h Wed Feb 21 23:42:19 2024 -0800 @@ -94,7 +94,7 @@ genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t system_opts, uint8_t force_region); genesis_context *alloc_config_genesis_cdboot(system_media *media, uint32_t system_opts, uint8_t force_region); -genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region); +genesis_context* alloc_config_pico(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t ym_opts, uint8_t force_region, system_type stype); void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc, uint8_t all); void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen); void gen_update_refresh_free_access(m68k_context *context); diff -r 09c9d2c6bac0 -r cb62730d5c99 system.c --- a/system.c Wed Feb 21 20:34:52 2024 -0800 +++ b/system.c Wed Feb 21 23:42:19 2024 -0800 @@ -41,6 +41,21 @@ ) { return SYSTEM_SMS; } + if (media->size > 400) { + uint8_t *buffer = media->buffer; + if (!memcmp(buffer + 4, "\x00\x00\x04\x00", 4) && (buffer[0x80] == 0 || buffer[0x80] == 0xFF)) { + int i = 0x81; + for(; i < 0x400; i++) + { + if (buffer[i] != buffer[0x80]) { + break; + } + } + if (i == 0x400) { + return SYSTEM_COPERA; + } + } + } if (safe_cmp("BLSTEL\x02", 0, media->buffer, media->size)) { uint8_t *buffer = media->buffer; if (media->size > 9 && buffer[7] == 0) { @@ -119,7 +134,8 @@ case SYSTEM_MEDIA_PLAYER: return &(alloc_media_player(media, opts))->header; case SYSTEM_PICO: - return &(alloc_config_pico(media->buffer, media->size, lock_on, lock_on_size, opts, force_region))->header; + case SYSTEM_COPERA: + return &(alloc_config_pico(media->buffer, media->size, lock_on, lock_on_size, opts, force_region, stype))->header; default: return NULL; } diff -r 09c9d2c6bac0 -r cb62730d5c99 system.h --- a/system.h Wed Feb 21 20:34:52 2024 -0800 +++ b/system.h Wed Feb 21 23:42:19 2024 -0800 @@ -19,7 +19,8 @@ SYSTEM_JAGUAR, SYSTEM_MEDIA_PLAYER, SYSTEM_COLECOVISION, - SYSTEM_PICO + SYSTEM_PICO, + SYSTEM_COPERA } system_type; typedef enum {