# HG changeset patch # User Michael Pavone # Date 1643610569 28800 # Node ID cc13c100b02741376422b8fb318f655fcb8dcc19 # Parent 3748a2a8a4b7812b61d3de5050ab806678becb0f# Parent 598017ef4b0d9ffa98079cabcac53c4e34dfcb0c Merge Sega CD branch now that it sort of works diff -r 3748a2a8a4b7 -r cc13c100b027 Makefile --- a/Makefile Sat Jan 01 18:54:46 2022 -0800 +++ b/Makefile Sun Jan 30 22:29:29 2022 -0800 @@ -181,7 +181,7 @@ M68KOBJS=68kinst.o ifdef NEW_CORE -Z80OBJS=z80.o z80inst.o +Z80OBJS=z80.o z80inst.o M68KOBJS+= m68k.o CFLAGS+= -DNEW_CORE else @@ -197,7 +197,7 @@ endif endif AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o -CONFIGOBJS=config.o tern.o util.o paths.o +CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o ifdef USE_FBDEV @@ -205,7 +205,7 @@ else RENDEROBJS+= render_sdl.o endif - + ifdef NOZLIB CFLAGS+= -DDISABLE_ZLIB else @@ -214,12 +214,14 @@ MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o \ + segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) - + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) \ + segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o + ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR else @@ -278,7 +280,7 @@ blastem$(EXE) : $(MAINOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS) $(FIXUP) ./$@ - + blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) $(CC) -o $@ $^ $(LDFLAGS) @@ -287,7 +289,7 @@ dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o $(CC) -o $@ $^ $(OPT) - + jagdis : jagdis.o jagcpu.o tern.o $(CC) -o $@ $^ @@ -327,7 +329,7 @@ test_arm : test_arm.o gen_arm.o mem.o gen.o $(CC) -o test_arm test_arm.o gen_arm.o mem.o gen.o - + test_int_timing : test_int_timing.o vdp.o $(CC) -o $@ $^ @@ -339,7 +341,7 @@ vos_prog_info : vos_prog_info.o vos_program_module.o $(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o - + m68k.c : m68k.cpu cpu_dsl.py ./cpu_dsl.py -d call $< > $@ @@ -354,7 +356,7 @@ %.o : %.c $(CC) $(CFLAGS) -c -o $@ $< - + %.o : %.m $(CC) $(CFLAGS) -c -o $@ $< diff -r 3748a2a8a4b7 -r cc13c100b027 backend_x86.c --- a/backend_x86.c Sat Jan 01 18:54:46 2022 -0800 +++ b/backend_x86.c Sun Jan 30 22:29:29 2022 -0800 @@ -116,7 +116,7 @@ if (after_inc) { *after_inc = code->cur; } - + if (opts->address_size == SZ_D && opts->address_mask != 0xFFFFFFFF) { and_ir(code, opts->address_mask, adr_reg, SZ_D); } else if (opts->address_size == SZ_W && opts->address_mask != 0xFFFF) { @@ -127,19 +127,31 @@ uint32_t ram_flags_off = opts->ram_flags_off; uint32_t min_address = 0; uint32_t max_address = opts->max_address; + uint8_t need_wide_jcc = 0; for (uint32_t chunk = 0; chunk < num_chunks; chunk++) { + code_info chunk_start = *code; if (memmap[chunk].start > min_address) { cmp_ir(code, memmap[chunk].start, adr_reg, opts->address_size); lb_jcc = code->cur + 1; - jcc(code, CC_C, code->cur + 2); + if (need_wide_jcc) { + jcc(code, CC_C, code->cur + 130); + lb_jcc++; + } else { + jcc(code, CC_C, code->cur + 2); + } } else { min_address = memmap[chunk].end; } if (memmap[chunk].end < max_address) { cmp_ir(code, memmap[chunk].end, adr_reg, opts->address_size); ub_jcc = code->cur + 1; - jcc(code, CC_NC, code->cur + 2); + if (need_wide_jcc) { + jcc(code, CC_NC, code->cur + 130); + ub_jcc++; + } else { + jcc(code, CC_NC, code->cur + 2); + } } else { max_address = memmap[chunk].start; } @@ -298,6 +310,33 @@ } retn(code); } + if (lb_jcc) { + if (need_wide_jcc) { + *((int32_t*)lb_jcc) = code->cur - (lb_jcc+4); + } else if (code->cur - (lb_jcc+1) > 0x7f) { + need_wide_jcc = 1; + chunk--; + *code = chunk_start; + continue; + } else { + *lb_jcc = code->cur - (lb_jcc+1); + } + lb_jcc = NULL; + } + if (ub_jcc) { + if (need_wide_jcc) { + *((int32_t*)ub_jcc) = code->cur - (ub_jcc+4); + } else if (code->cur - (ub_jcc+1) > 0x7f) { + need_wide_jcc = 1; + chunk--; + *code = chunk_start; + continue; + } else { + *ub_jcc = code->cur - (ub_jcc+1); + } + + ub_jcc = NULL; + } if (memmap[chunk].flags & MMAP_CODE) { uint32_t added_offset; if (memmap[chunk].mask == opts->address_mask) { @@ -311,13 +350,8 @@ ram_flags_off += 1; } } - if (lb_jcc) { - *lb_jcc = code->cur - (lb_jcc+1); - lb_jcc = NULL; - } - if (ub_jcc) { - *ub_jcc = code->cur - (ub_jcc+1); - ub_jcc = NULL; + if (need_wide_jcc) { + need_wide_jcc = 0; } } if (!is_write) { diff -r 3748a2a8a4b7 -r cc13c100b027 blastem.c --- a/blastem.c Sat Jan 01 18:54:46 2022 -0800 +++ b/blastem.c Sun Jan 30 22:29:29 2022 -0800 @@ -30,6 +30,7 @@ #include "bindings.h" #include "menu.h" #include "zip.h" +#include "cue.h" #include "event_log.h" #ifndef DISABLE_NUKLEAR #include "nuklear_ui/blastem_nuklear.h" @@ -91,7 +92,7 @@ size_t readsize = 0; uint16_t *dst, *buf; dst = buf = malloc(filesize); - + size_t read; do { @@ -107,9 +108,9 @@ } } while(read > 0); romclose(f); - + *buffer = buf; - + return readsize; } @@ -132,7 +133,7 @@ return 0; } -uint32_t load_rom_zip(const char *filename, void **dst) +uint32_t load_media_zip(const char *filename, system_media *dst) { static const char *valid_exts[] = {"bin", "md", "gen", "sms", "rom", "smd"}; const uint32_t num_exts = sizeof(valid_exts)/sizeof(*valid_exts); @@ -140,7 +141,7 @@ if (!z) { return 0; } - + for (uint32_t i = 0; i < z->num_entries; i++) { char *ext = path_extension(z->entries[i].name); @@ -151,20 +152,23 @@ { if (!strcasecmp(ext, valid_exts[j])) { size_t out_size = nearest_pow2(z->entries[i].size); - *dst = zip_read(z, i, &out_size); - if (*dst) { - if (is_smd_format(z->entries[i].name, *dst)) { + dst->buffer = zip_read(z, i, &out_size); + if (dst->buffer) { + if (is_smd_format(z->entries[i].name, dst->buffer)) { size_t offset; for (offset = 0; offset + SMD_BLOCK_SIZE + SMD_HEADER_SIZE <= out_size; offset += SMD_BLOCK_SIZE) { uint8_t tmp[SMD_BLOCK_SIZE]; - uint8_t *u8dst = *dst; + uint8_t *u8dst = dst->buffer; memcpy(tmp, u8dst + offset + SMD_HEADER_SIZE, SMD_BLOCK_SIZE); process_smd_block((void *)(u8dst + offset), tmp, SMD_BLOCK_SIZE); } out_size = offset; } - free(ext); + dst->extension = ext; + dst->dir = path_dirname(filename); + dst->name = basename_no_extension(filename); + dst->size = out_size; zip_close(z); return out_size; } @@ -176,13 +180,13 @@ return 0; } -uint32_t load_rom(const char * filename, void **dst, system_type *stype) +uint32_t load_media(const char * filename, system_media *dst, system_type *stype) { uint8_t header[10]; char *ext = path_extension(filename); if (ext && !strcasecmp(ext, "zip")) { free(ext); - return load_rom_zip(filename, dst); + return load_media_zip(filename, dst); } free(ext); ROMFILE f = romopen(filename, "rb"); @@ -192,42 +196,56 @@ if (sizeof(header) != romread(header, 1, sizeof(header), f)) { fatal_error("Error reading from %s\n", filename); } - + + uint32_t ret = 0; if (is_smd_format(filename, header)) { - if (stype) { - *stype = SYSTEM_GENESIS; + if (stype) { + *stype = SYSTEM_GENESIS; + } + ret = load_smd_rom(f, &dst->buffer); } - return load_smd_rom(f, dst); - } - - size_t filesize = 512 * 1024; - size_t readsize = sizeof(header); - - char *buf = malloc(filesize); - memcpy(buf, header, readsize); - - size_t read; - do { - read = romread(buf + readsize, 1, filesize - readsize, f); - if (read > 0) { - readsize += read; - if (readsize == filesize) { - int one_more = romgetc(f); - if (one_more >= 0) { - filesize *= 2; - buf = realloc(buf, filesize); - buf[readsize++] = one_more; - } else { - read = 0; + + if (!ret) { + size_t filesize = 512 * 1024; + size_t readsize = sizeof(header); + + char *buf = malloc(filesize); + memcpy(buf, header, readsize); + + size_t read; + do { + read = romread(buf + readsize, 1, filesize - readsize, f); + if (read > 0) { + readsize += read; + if (readsize == filesize) { + int one_more = romgetc(f); + if (one_more >= 0) { + filesize *= 2; + buf = realloc(buf, filesize); + buf[readsize++] = one_more; + } else { + read = 0; + } } } + } while (read > 0); + dst->buffer = buf; + ret = (uint32_t)readsize; + } + dst->dir = path_dirname(filename); + dst->name = basename_no_extension(filename); + dst->extension = path_extension(filename); + dst->size = ret; + romclose(f); + if (!strcasecmp(dst->extension, "cue")) { + if (parse_cue(dst)) { + if (stype) { + *stype = SYSTEM_SEGACD; + } } - } while (read > 0); - - *dst = buf; - - romclose(f); - return readsize; + } + + return ret; } @@ -389,10 +407,7 @@ free(lock_on.dir); free(lock_on.name); free(lock_on.extension); - lock_on.dir = path_dirname(lock_on_path); - lock_on.name = basename_no_extension(lock_on_path); - lock_on.extension = path_extension(lock_on_path); - lock_on.size = load_rom(lock_on_path, &lock_on.buffer, NULL); + load_media(lock_on_path, &lock_on, NULL); } static uint32_t opts = 0; @@ -411,16 +426,14 @@ //start a new arena and save old one in suspended system context current_system->arena = start_new_arena(); } - system_type stype = SYSTEM_UNKNOWN; - if (!(cart.size = load_rom(path, &cart.buffer, &stype))) { - fatal_error("Failed to open %s for reading\n", path); - } free(cart.dir); free(cart.name); free(cart.extension); - cart.dir = path_dirname(path); - cart.name = basename_no_extension(path); - cart.extension = path_extension(path); + system_type stype = SYSTEM_UNKNOWN; + if (!(cart.size = load_media(path, &cart, &stype))) { + fatal_error("Failed to open %s for reading\n", path); + } + if (force_stype != SYSTEM_UNKNOWN) { stype = force_stype; } @@ -572,12 +585,9 @@ if (i >= argc) { fatal_error("-o must be followed by a lock on cartridge filename\n"); } - lock_on.size = load_rom(argv[i], &lock_on.buffer, NULL); - if (!lock_on.size) { + if (!load_media(argv[i], &lock_on, NULL)) { fatal_error("Failed to load lock on cartridge %s\n", argv[i]); } - lock_on.name = basename_no_extension(argv[i]); - lock_on.extension = path_extension(argv[i]); cart.chain = &lock_on; break; } @@ -611,12 +621,9 @@ if (reader_port) { reader_addr = argv[i]; } else { - if (!(cart.size = load_rom(argv[i], &cart.buffer, stype == SYSTEM_UNKNOWN ? &stype : NULL))) { - fatal_error("Failed to open %s for reading\n", argv[i]); - } - cart.dir = path_dirname(argv[i]); - cart.name = basename_no_extension(argv[i]); - cart.extension = path_extension(argv[i]); + if (!load_media(argv[i], &cart, stype == SYSTEM_UNKNOWN ? &stype : NULL)) { + fatal_error("Failed to open %s for reading\n", argv[i]); + } } romfname = argv[i]; loaded = 1; @@ -626,7 +633,7 @@ height = atoi(argv[i]); } } - + int def_width = 0, def_height = 0; char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval; if (config_width) { @@ -657,7 +664,7 @@ render_set_drag_drop_handler(on_drag_drop); } set_bindings(); - + uint8_t menu = !loaded; uint8_t use_nuklear = 0; #ifndef DISABLE_NUKLEAR @@ -670,7 +677,7 @@ romfname = "menu.bin"; } if (is_absolute_path(romfname)) { - if (!(cart.size = load_rom(romfname, &cart.buffer, &stype))) { + if (!(cart.size = load_media(romfname, &cart, &stype))) { fatal_error("Failed to open UI ROM %s for reading", romfname); } } else { @@ -683,12 +690,12 @@ cart.buffer = realloc(cart.buffer, rom_size); cart.size = rom_size; } + cart.dir = path_dirname(romfname); + cart.name = basename_no_extension(romfname); + cart.extension = path_extension(romfname); } //force system detection, value on command line is only for games not the menu stype = detect_system_type(&cart); - cart.dir = path_dirname(romfname); - cart.name = basename_no_extension(romfname); - cart.extension = path_extension(romfname); loaded = 1; } char *state_format = tern_find_path(config, "ui\0state_format\0", TVAL_PTR).ptrval; @@ -705,12 +712,12 @@ if (stype == SYSTEM_UNKNOWN) { fatal_error("Failed to detect system type for %s\n", romfname); } - + current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region); if (!current_system) { fatal_error("Failed to configure emulated machine for %s\n", romfname); } - + setup_saves(&cart, current_system); update_title(current_system->info.name); if (menu) { @@ -719,7 +726,7 @@ game_system = current_system; } } - + #ifndef DISABLE_NUKLEAR if (use_nuklear) { blastem_nuklear_init(!menu); @@ -740,7 +747,7 @@ setup_saves(&cart, current_system); update_title(current_system->info.name); } - + current_system->debugger_type = dtype; current_system->enter_debugger = start_in_debugger && menu == debug_target; current_system->start_context(current_system, menu ? NULL : statefile); diff -r 3748a2a8a4b7 -r cc13c100b027 cd_graphics.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cd_graphics.c Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,286 @@ +#include "cd_graphics.h" +#include "backend.h" + +void cd_graphics_init(segacd_context *cd) +{ + cd->graphics_int_cycle = CYCLE_NEVER; +} + +#define BIT_HFLIP 0x8000 + +static uint8_t get_src_pixel(segacd_context *cd) +{ + uint16_t x = cd->graphics_x >> 11; + uint16_t y = cd->graphics_y >> 11; + cd->graphics_x += cd->graphics_dx; + cd->graphics_x &= 0xFFFFFF; + cd->graphics_y += cd->graphics_dy; + cd->graphics_y &= 0xFFFFFF; + uint16_t stamp_shift, pixel_mask; + uint16_t stamp_num_mask; + if (cd->gate_array[GA_STAMP_SIZE] & BIT_STS) { + //32x32 stamps + stamp_shift = 5; + pixel_mask = 0x1F; + stamp_num_mask = 0x3FC; + } else { + //16x16 stamps + stamp_shift = 4; + pixel_mask = 0xF; + stamp_num_mask = 0x3FF; + } + uint16_t stamp_x = x >> stamp_shift; + uint16_t stamp_y = y >> stamp_shift; + uint16_t max, base_mask; + uint32_t row_shift; + if (cd->gate_array[GA_STAMP_SIZE] & BIT_SMS) { + max = 4096 >> stamp_shift; + base_mask = 0xE000 << ((5 - stamp_shift) << 1); + //128 stamps in 32x32 mode, 256 stamps in 16x16 mode + row_shift = 12 - stamp_shift; + } else { + max = 256 >> stamp_shift; + base_mask = 0xFFE0 << ((5 - stamp_shift) << 1); + //8 stamps in 32x32 mode, 16 stamps in 16x16 mode + row_shift = 8 - stamp_shift; + } + if (stamp_x > max || stamp_y > max) { + if (cd->gate_array[GA_STAMP_SIZE] & BIT_RPT) { + stamp_x &= max - 1; + stamp_y &= max - 1; + } else { + return 0; + } + } + uint32_t address = (cd->gate_array[GA_STAMP_MAP_BASE] & base_mask) << 1; + address += (stamp_y << row_shift) + stamp_x; + uint16_t stamp_def = cd->word_ram[address]; + uint16_t stamp_num = stamp_def & stamp_num_mask; + if (!stamp_num) { + //manual says stamp 0 can't be used, I assume that means it's treated as transparent + return 0; + } + uint16_t pixel_x = x & pixel_mask; + uint16_t pixel_y = y & pixel_mask; + if (stamp_def & BIT_HFLIP) { + pixel_x = pixel_mask - pixel_x; + } + uint16_t tmp; + switch (stamp_def >> 13 & 3) + { + case 0: + break; + case 1: + tmp = pixel_y; + pixel_y = pixel_x; + pixel_x = pixel_mask - tmp; + break; + case 2: + tmp = pixel_y; + pixel_y = pixel_mask - pixel_x; + pixel_x = pixel_mask - tmp; + break; + case 3: + tmp = pixel_y; + pixel_y = pixel_mask - pixel_x; + pixel_x = tmp; + break; + } + uint16_t cell_x = pixel_x >> 3; + uint32_t pixel_address = stamp_num << 6; + pixel_address += (pixel_y << 1) + (cell_x << (stamp_shift + 1)) + (pixel_x >> 2 & 1); + uint16_t word = cd->word_ram[pixel_address]; + switch (pixel_x & 3) + { + default: + case 0: + return word >> 12; + case 1: + return word >> 8 & 0xF; + case 2: + return word >> 4 & 0xF; + case 3: + return word & 0xF; + } + +} + +enum { + FETCH_X, + FETCH_Y, + FETCH_DX, + FETCH_DY, + PIXEL0, + PIXEL1, + PIXEL2, + PIXEL3, + DRAW +}; + +void draw_pixels(segacd_context *cd) +{ + uint16_t to_draw = 4 - (cd->graphics_dst_x & 3); + uint16_t x_end = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3); + if (cd->graphics_dst_x + to_draw > x_end) { + to_draw = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3) - cd->graphics_dst_x; + } + for(uint16_t i = 0; i < to_draw; i++) + { + uint32_t dst_address = cd->gate_array[GA_IMAGE_BUFFER_START] << 1; + dst_address += cd->graphics_dst_y << 1; + dst_address += cd->graphics_dst_x >> 2 & 1; + dst_address += ((cd->graphics_dst_x >> 3) * cd->gate_array[GA_IMAGE_BUFFER_VCELLS]) << 4; + uint16_t pixel_shift = 12 - 4 * (cd->graphics_dst_x & 3); + uint16_t pixel = cd->graphics_pixels[i] << pixel_shift; + uint16_t src_mask_check = 0xF << pixel_shift; + uint16_t src_mask_keep = ~src_mask_check; + pixel &= src_mask_check; + switch (cd->gate_array[1] >> 3 & 3) + { + case 0: + //priority mode off + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + break; + case 1: + //underwrite + if (pixel && ! (cd->word_ram[dst_address] & src_mask_check)) { + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + } + break; + case 3: + //overwrite + if (pixel) { + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + } + break; + } + cd->graphics_dst_x++; + } + if (cd->graphics_dst_x == x_end) { + cd->graphics_dst_y++; + --cd->gate_array[GA_IMAGE_BUFFER_LINES]; + cd->gate_array[GA_TRACE_VECTOR_BASE] += 2; + cd->graphics_step = FETCH_X; + } else { + cd->graphics_step = PIXEL0; + } +} + +#define CHECK_CYCLES cd->graphics_step++; if(cd->graphics_cycle >= cycle) break +#define CHECK_ONLY if(cd->graphics_cycle >= cycle) break + +static void do_graphics(segacd_context *cd, uint32_t cycle) +{ + if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { + return; + } + while (cd->graphics_cycle < cycle) + { + switch (cd->graphics_step) + { + case FETCH_X: + cd->graphics_x = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1] << 8; + cd->graphics_cycle += 3*4; + cd->graphics_dst_x = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; + CHECK_CYCLES; + case FETCH_Y: + cd->graphics_y = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 1] << 8; + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case FETCH_DX: + cd->graphics_dx = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 2]; + if (cd->graphics_dx & 0x8000) { + cd->graphics_dx |= 0xFF0000; + } + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case FETCH_DY: + cd->graphics_dy = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 3]; + if (cd->graphics_dy & 0x8000) { + cd->graphics_dy |= 0xFF0000; + } + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case PIXEL0: + cd->graphics_pixels[0] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 3 || (cd->graphics_dst_x + 1 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL1: + cd->graphics_pixels[1] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 2 || (cd->graphics_dst_x + 2 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL2: + cd->graphics_pixels[2] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 1 || (cd->graphics_dst_x + 3 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL3: + cd->graphics_pixels[3] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case DRAW: + draw_pixels(cd); + cd->graphics_cycle += 1*4; + if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { + break; + } + CHECK_ONLY; + } + } +} + +void cd_graphics_run(segacd_context *cd, uint32_t cycle) +{ + while (cd->graphics_cycle < cycle) + { + if (cd->gate_array[GA_STAMP_SIZE] & BIT_GRON) { + do_graphics(cd, cycle); + //end calculation and actual emulated execution time probably don't 100% line up yet + //deal with that here for now + for(; cd->graphics_cycle < cycle; cd->graphics_cycle += 4) + { + } + if (cd->graphics_cycle >= cd->graphics_int_cycle) { + cd->gate_array[GA_STAMP_SIZE] &= ~BIT_GRON; + break; + } + } else { + cd->graphics_cycle = cycle; + } + } +} +void cd_graphics_start(segacd_context *cd) +{ + if (!(cd->gate_array[GA_STAMP_SIZE] & BIT_GRON)) { + printf("grahpics start @ %u\n", cd->graphics_cycle); + cd->gate_array[GA_STAMP_SIZE] |= BIT_GRON; + //Manual scan is bad, but formula appears to be + // vsize * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)) + //with an additional 13? cycle setup cost per line + uint32_t lines = cd->gate_array[GA_IMAGE_BUFFER_LINES]; + uint32_t hdots = cd->gate_array[GA_IMAGE_BUFFER_HDOTS]; + uint32_t hoffset = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; + cd->graphics_int_cycle = cd->graphics_cycle + 4 * lines * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)); + cd->graphics_dst_y = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] >> 3; + } else { + printf("graphics start ignored @ %u\n", cd->graphics_cycle); + } + +} diff -r 3748a2a8a4b7 -r cc13c100b027 cd_graphics.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cd_graphics.h Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,27 @@ +#ifndef CD_GRAPHICS_H_ +#define CD_GRAPHICS_H_ + +#include "segacd.h" + +enum { + GA_STAMP_SIZE = 0x58/2, + GA_STAMP_MAP_BASE, + GA_IMAGE_BUFFER_VCELLS, + GA_IMAGE_BUFFER_START, + GA_IMAGE_BUFFER_OFFSET, + GA_IMAGE_BUFFER_HDOTS, + GA_IMAGE_BUFFER_LINES, + GA_TRACE_VECTOR_BASE +}; + +//GA_STAMP_SIZE +#define BIT_GRON 0x8000 +#define BIT_SMS 0x0004 +#define BIT_STS 0x0002 +#define BIT_RPT 0x0001 + +void cd_graphics_init(segacd_context *cd); +void cd_graphics_run(segacd_context *cd, uint32_t cycle); +void cd_graphics_start(segacd_context *cd); + +#endif //CD_GRAPHICS_H_ diff -r 3748a2a8a4b7 -r cc13c100b027 cdd_mcu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdd_mcu.c Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,596 @@ +#include "cdd_mcu.h" +#include "backend.h" + +#define SCD_MCLKS 50000000 +#define CD_BLOCK_CLKS 16934400 +#define CDD_MCU_DIVIDER 8 +#define SECTOR_CLOCKS (CD_BLOCK_CLKS/75) +#define NIBBLE_CLOCKS (CDD_MCU_DIVIDER * 77) +#define BYTE_CLOCKS (SECTOR_CLOCKS/2352) // 96 + +//lead in start max diameter 46 mm +//program area start max diameter 50 mm +//difference 4 mm = 4000 um +//radius difference 2 mm = 2000 um +//track pitch 1.6 um +//1250 physical tracks in between +//linear speed 1.2 m/s - 1.4 m/s +// 1.3 m = 1300 mm +// circumference at 46 mm ~ 144.51 mm +// circumference at 50 mm ~ 157.08 mm +// avg is 150.795 +// 75 sectors per second +// 17.3333 mm "typical" length of a sector +// ~8.7 sectors per track in lead-in area +#define LEADIN_SECTORS 10875 + +static uint32_t cd_block_to_mclks(uint32_t cycles) +{ + return ((uint64_t)cycles) * ((uint64_t)SCD_MCLKS) / ((uint64_t)CD_BLOCK_CLKS); +} + +static uint32_t mclks_to_cd_block(uint32_t cycles) +{ + return ((uint64_t)cycles) * ((uint64_t)CD_BLOCK_CLKS) / ((uint64_t)SCD_MCLKS); +} + +void cdd_mcu_init(cdd_mcu *context, system_media *media) +{ + context->next_int_cycle = CYCLE_NEVER; + context->last_subcode_cycle = CYCLE_NEVER; + context->last_nibble_cycle = CYCLE_NEVER; + context->requested_format = SF_NOTREADY; + context->media = media; + context->current_status_nibble = -1; + context->current_cmd_nibble = -1; +} + +enum { + GAO_CDD_CTRL, + GAO_CDD_STATUS, + GAO_CDD_CMD = GAO_CDD_STATUS+5 +}; + +static uint8_t checksum(uint8_t *vbuffer) +{ + uint8_t *buffer = vbuffer; + uint8_t sum = 0; + for (int i = 0; i < 9; i++) + { + sum += buffer[i]; + } + return (~sum) & 0xF; +} +#define COARSE_SEEK 2200 //made up numbers +#define FINE_SEEK 10 +static void handle_seek(cdd_mcu *context) +{ + //TODO: more realistic seeking behavior + if (context->seeking) { + if (context->seek_pba == context->head_pba) { + context->seeking = 0; + } else if (context->seek_pba > context->head_pba) { + if (context->seek_pba - context->head_pba >= COARSE_SEEK) { + context->head_pba += COARSE_SEEK; + } else if (context->seek_pba - context->head_pba >= FINE_SEEK) { + context->head_pba += FINE_SEEK; + } else { + context->head_pba++; + } + } else { + if (context->head_pba - context->seek_pba >= COARSE_SEEK) { + context->head_pba -= COARSE_SEEK; + } else if (context->head_pba >= FINE_SEEK) { + context->head_pba -= FINE_SEEK; + } else { + context->head_pba = 0; + } + } + } +} + +static void lba_to_status(cdd_mcu *context, uint32_t lba) +{ + uint32_t seconds = lba / 75; + uint32_t frames = lba % 75; + uint32_t minutes = seconds / 60; + seconds = seconds % 60; + context->status_buffer.b.time.min_high = minutes / 10; + context->status_buffer.b.time.min_low = minutes % 10; + context->status_buffer.b.time.sec_high = seconds / 10; + context->status_buffer.b.time.sec_low = seconds % 10; + context->status_buffer.b.time.frame_high = frames / 10; + context->status_buffer.b.time.frame_low = frames % 10; +} + +static void update_status(cdd_mcu *context) +{ + switch (context->status) + { + case DS_PLAY: + handle_seek(context); + if (!context->seeking) { + context->head_pba++; + uint32_t lba = context->head_pba - LEADIN_SECTORS; + for (uint32_t i = 0; i < context->media->num_tracks; i++) + { + if (lba < context->media->tracks[i].fake_pregap) { + context->in_fake_pregap = 1; + break; + } + lba -= context->media->tracks[i].fake_pregap; + if (lba < context->media->tracks[i].start_lba) { + context->in_fake_pregap = 1; + break; + } + if (lba < context->media->tracks[i].end_lba) { + fseek(context->media->f, lba * 2352, SEEK_SET); + context->in_fake_pregap = 0; + break; + } + } + } + break; + case DS_PAUSE: + handle_seek(context); + break; + case DS_TOC_READ: + handle_seek(context); + if (!context->seeking) { + context->head_pba++; + if (context->media && context->media->type == MEDIA_CDROM && context->media->num_tracks) { + if (context->head_pba > 3*context->media->num_tracks + 1) { + context->toc_valid = 1; + context->seeking = 1; + context->seek_pba = LEADIN_SECTORS + context->media->tracks[0].start_lba; + context->status = DS_PAUSE; + } + + } else { + context->status = DS_NO_DISC; + } + } + break; + + } + if (context->first_cmd_received) { + switch (context->requested_format) + { + case SF_ABSOLUTE: + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { + lba_to_status(context, context->head_pba - LEADIN_SECTORS); + context->status_buffer.format = SF_ABSOLUTE; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_RELATIVE: + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { + uint32_t lba =context->head_pba - LEADIN_SECTORS; + for (uint32_t i = 0; i < context->media->num_tracks; i++) + { + if (lba < context->media->tracks[i].end_lba) { + if (context->media->tracks[i].fake_pregap) { + if (lba > context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } else { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].fake_pregap - lba; + break; + } + } + if (lba < context->media->tracks[i].start_lba) { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].start_lba - lba; + } else { + lba -= context->media->tracks[i].start_lba; + } + break; + } else if (context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } + } + lba_to_status(context, lba); + context->status_buffer.format = SF_RELATIVE; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TRACK: + if (context->toc_valid && context->head_pba >= LEADIN_SECTORS) { + uint32_t lba =context->head_pba - LEADIN_SECTORS; + uint32_t i; + for (i = 0; i < context->media->num_tracks; i++) + { + if (lba < context->media->tracks[i].end_lba) { + if (context->media->tracks[i].fake_pregap) { + if (lba > context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } else { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].fake_pregap - lba; + break; + } + } + if (lba < context->media->tracks[i].start_lba) { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].start_lba - lba; + } else { + lba -= context->media->tracks[i].start_lba; + } + break; + } else if (context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } + } + context->status_buffer.b.track.track_high = (i + 1) / 10; + context->status_buffer.b.track.track_low = (i + 1) % 10; + if (context->media->tracks[i].type == TRACK_DATA) { + context->status_buffer.b.track.control = 4; + } else { + //TODO: pre-emphasis flag + //TODO: copy permitted flag + context->status_buffer.b.track.control = 0; + } + context->status_buffer.b.track.adr = 1; + context->status_buffer.format = SF_TRACK; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCO: + if (context->toc_valid) { + uint32_t total_fake_pregap = 0; + for (uint32_t i = 0; i < context->media->num_tracks; i++) + { + total_fake_pregap += context->media->tracks[i].fake_pregap; + } + lba_to_status(context, context->media->tracks[context->media->num_tracks - 1].end_lba + total_fake_pregap); + context->status_buffer.format = SF_TOCO; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCT: + if (context->toc_valid) { + context->status_buffer.b.toct.first_track_high = 0; + context->status_buffer.b.toct.first_track_low = 1; + context->status_buffer.b.toct.last_track_high = (context->media->num_tracks + 1) / 10; + context->status_buffer.b.toct.last_track_low = (context->media->num_tracks + 1) % 10; + context->status_buffer.b.toct.version = 0; + context->status_buffer.format = SF_TOCT; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCN: + if (context->toc_valid) { + uint32_t lba = context->media->tracks[context->requested_track - 1].start_lba; + for (uint32_t i = 0; i < context->requested_track; i++) { + lba += context->media->tracks[i].fake_pregap; + } + lba_to_status(context, lba); + if (context->media->tracks[context->requested_track - 1].type == TRACK_DATA) { + context->status_buffer.b.tocn.frame_low |= 0x80; + } + context->status_buffer.b.tocn.track_low = context->requested_track % 10; + context->status_buffer.format = SF_TOCN; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_NOTREADY: + memset(&context->status_buffer, 0, sizeof(context->status_buffer) - 1); + context->status_buffer.format = SF_NOTREADY; + break; + } + if (context->error_status == DS_STOP) { + if (context->requested_format >= SF_TOCO && context->requested_format <= SF_TOCN) { + context->status_buffer.status = DS_TOC_READ; + } else if (context->seeking) { + context->status_buffer.status = DS_SEEK; + } else { + context->status_buffer.status = context->status; + } + } else { + context->status_buffer.status = context->error_status; + context->status_buffer.format = SF_NOTREADY; + context->error_status = DS_STOP; + } + if (context->requested_format != SF_TOCN) { + context->status_buffer.b.time.flags = 1; //TODO: populate these + } + } else { + // Did not receive our first command so just send zeroes + memset(&context->status_buffer, 0, sizeof(context->status_buffer) - 1); + } + context->status_buffer.checksum = checksum((uint8_t *)&context->status_buffer); + if (context->status_buffer.format != SF_NOTREADY) { + printf("CDD Status %X%X.%X%X%X%X%X%X.%X%X\n", + context->status_buffer.status, context->status_buffer.format, + context->status_buffer.b.time.min_high, context->status_buffer.b.time.min_low, + context->status_buffer.b.time.sec_high, context->status_buffer.b.time.sec_low, + context->status_buffer.b.time.frame_high, context->status_buffer.b.time.frame_low, + context->status_buffer.b.time.flags, context->status_buffer.checksum + ); + } +} + +static void run_command(cdd_mcu *context) +{ + uint8_t check = checksum((uint8_t*)&context->cmd_buffer); + printf("cmd %X, checksum: %X, calc: %X\n", context->cmd_buffer.cmd_type, context->cmd_buffer.checksum, check); + if (check != context->cmd_buffer.checksum) { + context->error_status = DS_SUM_ERROR; + return; + } + if (context->cmd_buffer.must_be_zero) { + context->error_status = DS_CMD_ERROR; + return; + } + context->first_cmd_received = 1; + switch (context->cmd_buffer.cmd_type) + { + case CMD_NOP: + break; + case CMD_STOP: + puts("CDD CMD: STOP"); + context->status = DS_STOP; + context->requested_format = SF_ABSOLUTE; + break; + case CMD_READ: + case CMD_SEEK: { + if (context->status == DS_DOOR_OPEN || context->status == DS_TRAY_MOVING || context->status == DS_DISC_LEADOUT || context->status == DS_DISC_LEADIN) { + context->error_status = DS_CMD_ERROR; + break; + } + if (context->requested_format == SF_TOCT || context->requested_format == SF_TOCN) { + context->requested_format = SF_ABSOLUTE; + } + if (!context->toc_valid) { + context->error_status = DS_CMD_ERROR; + break; + } + uint32_t lba = context->cmd_buffer.b.time.min_high * 10 + context->cmd_buffer.b.time.min_low; + lba *= 60; + lba += context->cmd_buffer.b.time.sec_high * 10 + context->cmd_buffer.b.time.sec_low; + lba *= 75; + lba += context->cmd_buffer.b.time.frame_high * 10 + context->cmd_buffer.b.time.frame_low; + printf("CDD CMD: %s cmd for lba %d, MM:SS:FF %u%u:%u%u:%u%u\n", + context->cmd_buffer.cmd_type == CMD_READ ? "READ" : "SEEK", lba, + context->cmd_buffer.b.time.min_high, context->cmd_buffer.b.time.min_low, + context->cmd_buffer.b.time.sec_high, context->cmd_buffer.b.time.sec_low, + context->cmd_buffer.b.time.frame_high, context->cmd_buffer.b.time.frame_low + ); + if (lba >= context->media->tracks[0].fake_pregap + context->media->tracks[context->media->num_tracks - 1].end_lba) { + context->error_status = DS_CMD_ERROR; + break; + } + context->seek_pba = lba + LEADIN_SECTORS - 4; + context->seeking = 1; + context->status = context->cmd_buffer.cmd_type == CMD_READ ? DS_PLAY : DS_PAUSE; + break; + } + case CMD_REPORT_REQUEST: + switch (context->cmd_buffer.b.format.status_type) + { + case SF_ABSOLUTE: + case SF_RELATIVE: + case SF_TRACK: + context->requested_format = context->cmd_buffer.b.format.status_type; + break; + case SF_TOCO: + if (context->toc_valid) { + context->requested_format = SF_TOCO; + } else { + context->error_status = DS_CMD_ERROR; + context->requested_format = SF_ABSOLUTE; + } + break; + case SF_TOCT: + if (context->toc_valid) { + if (context->status == DS_STOP) { + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + } + } else { + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + } + context->requested_format = SF_TOCT; + break; + case SF_TOCN: + context->requested_track = context->cmd_buffer.b.format.track_high * 10; + context->requested_track += context->cmd_buffer.b.format.track_low; + if (!context->media || context->requested_track > context->media->num_tracks) { + context->requested_format = SF_ABSOLUTE; + context->error_status = DS_CMD_ERROR; + } + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + context->requested_format = SF_TOCN; + break; + } + printf("CDD CMD: REPORT REQUEST(%d), format set to %d\n", context->cmd_buffer.b.format.status_type, context->requested_format); + break; + case CMD_PAUSE: + if (context->status == DS_DOOR_OPEN || context->status == DS_TRAY_MOVING || context->status == DS_DISC_LEADOUT || context->status == DS_DISC_LEADIN) { + context->error_status = DS_CMD_ERROR; + break; + } + if (context->requested_format == SF_TOCT || context->requested_format == SF_TOCN) { + context->requested_format = SF_ABSOLUTE; + } + if (!context->toc_valid) { + context->error_status = DS_CMD_ERROR; + break; + } + if (context->status == DS_STOP) { + context->seeking = 1; + context->seek_pba = LEADIN_SECTORS + context->media->tracks[0].fake_pregap + context->media->tracks[0].start_lba; + printf("CDD CMD: PAUSE, seeking to %u\n", context->seek_pba); + } else { + puts("CDD CMD: PAUSE"); + } + context->status = DS_PAUSE; + break; + default: + printf("CDD CMD: Unimplemented(%d)\n", context->cmd_buffer.cmd_type); + } +} + +#define BIT_HOCK 0x4 +#define BIT_DRS 0x2 +#define BIT_DTS 0x1 + +void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc) +{ + uint32_t cd_cycle = mclks_to_cd_block(cycle); + if (!(gate_array[GAO_CDD_CTRL] & BIT_HOCK)) { + //it's a little unclear if this gates the actual cd block clock or just handshaking + //assum it's actually the clock for now + context->cycle = cd_cycle; + return; + } + uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS; + uint32_t next_nibble = context->current_status_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; + uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; + uint32_t next_byte = context->current_sector_byte >= 0 ? context->last_byte_cycle + BYTE_CLOCKS : CYCLE_NEVER; + for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) + { + if (context->cycle >= next_subcode) { + context->last_subcode_cycle = context->cycle; + next_subcode = context->cycle + SECTOR_CLOCKS; + update_status(context); + next_nibble = context->cycle; + context->current_status_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DRS; + if (context->status == DS_PLAY && !context->seeking) { + next_byte = context->cycle; + context->current_sector_byte = 0; + } + } + if (context->cycle >= next_nibble) { + if (context->current_status_nibble == sizeof(cdd_status)) { + context->current_status_nibble = -1; + gate_array[GAO_CDD_STATUS] &= ~BIT_DRS; + if (context->cmd_recv_pending) { + context->cmd_recv_pending = 0; + context->current_cmd_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DTS; + next_nibble = context->cycle + NIBBLE_CLOCKS; + } else { + context->cmd_recv_wait = 1; + next_nibble = CYCLE_NEVER; + } + } else { + uint8_t value = ((uint8_t *)&context->status_buffer)[context->current_status_nibble]; + int ga_index = GAO_CDD_STATUS + (context->current_status_nibble >> 1); + if (context->current_status_nibble & 1) { + gate_array[ga_index] = value | (gate_array[ga_index] & 0xFF00); + } else { + gate_array[ga_index] = (value << 8) | (gate_array[ga_index] & 0x00FF); + } + if (context->current_status_nibble == 7) { + context->int_pending = 1; + context->next_int_cycle = cd_block_to_mclks(cycle + SECTOR_CLOCKS); + } + context->current_status_nibble++; + context->last_nibble_cycle = context->cycle; + next_nibble = context->cycle + NIBBLE_CLOCKS; + } + } else if (context->cycle >= next_cmd_nibble) { + if (context->current_cmd_nibble == sizeof(cdd_cmd)) { + next_cmd_nibble = CYCLE_NEVER; + context->current_cmd_nibble = -1; + gate_array[GAO_CDD_STATUS] &= ~BIT_DTS; + run_command(context); + } else { + int ga_index = GAO_CDD_CMD + (context->current_cmd_nibble >> 1); + uint8_t value = (context->current_cmd_nibble & 1) ? gate_array[ga_index] : gate_array[ga_index] >> 8; + ((uint8_t *)&context->cmd_buffer)[context->current_cmd_nibble] = value; + context->current_cmd_nibble++; + context->last_nibble_cycle = context->cycle; + next_cmd_nibble = context->cycle + NIBBLE_CLOCKS; + } + } + if (context->cycle >= next_byte) { + uint8_t byte; + if (context->in_fake_pregap) { + if (!context->current_sector_byte || (context->current_sector_byte >= 16)) { + byte = 0; + //TODO: error detection and correction bytes + } else if (context->current_sector_byte < 12) { + byte = 0xFF; + } else if (context->current_sector_byte == 12) { + uint32_t minute = ((context->head_pba - LEADIN_SECTORS) / 75) / 60; + byte = (minute % 10) | ((minute / 10 ) << 4); + } else if (context->current_sector_byte == 13) { + uint32_t seconds = ((context->head_pba - LEADIN_SECTORS) / 75) % 60; + byte = (seconds % 10) | ((seconds / 10 ) << 4); + } else if (context->current_sector_byte == 14) { + uint32_t frames = (context->head_pba - LEADIN_SECTORS) % 75; + byte = (frames % 10) | ((frames / 10 ) << 4); + } else { + byte = 1; + } + } else { + byte = fgetc(context->media->f); + } + lc8951_write_byte(cdc, cd_block_to_mclks(context->cycle), context->current_sector_byte++, byte); + context->last_byte_cycle = context->cycle; + next_byte = context->cycle + BYTE_CLOCKS; + if (context->current_sector_byte == 2352) { + context->current_sector_byte = -1; + + } + } + } +} + +void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array) +{ + if (context->cmd_recv_wait) { + context->current_cmd_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DTS; + context->last_nibble_cycle = context->cycle; + context->cmd_recv_wait = 0; + } else { + context->cmd_recv_pending = 1; + } +} + +void cdd_hock_enabled(cdd_mcu *context) +{ + context->last_subcode_cycle = context->cycle; + context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS + 7 * NIBBLE_CLOCKS); +} + +void cdd_hock_disabled(cdd_mcu *context) +{ + context->last_subcode_cycle = CYCLE_NEVER; + context->next_int_cycle = CYCLE_NEVER; + context->last_nibble_cycle = CYCLE_NEVER; + context->current_status_nibble = -1; + context->current_cmd_nibble = -1; +} + +void cdd_mcu_adjust_cycle(cdd_mcu *context, uint32_t deduction) +{ + uint32_t cd_deduction = mclks_to_cd_block(deduction); + if (context->next_int_cycle != CYCLE_NEVER) { + context->next_int_cycle -= deduction; + } + if (context->last_subcode_cycle != CYCLE_NEVER) { + context->last_subcode_cycle -= cd_deduction; + } + if (context->last_nibble_cycle != CYCLE_NEVER) { + context->last_nibble_cycle -= cd_deduction; + } + if (context->last_byte_cycle != CYCLE_NEVER) { + context->last_byte_cycle -= cd_deduction; + } +} diff -r 3748a2a8a4b7 -r cc13c100b027 cdd_mcu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdd_mcu.h Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,168 @@ +#ifndef CDD_MCU_H_ +#define CDD_MCU_H_ +#include "system.h" +#include "lc8951.h" + +typedef enum { + SF_ABSOLUTE, + SF_RELATIVE, + SF_TRACK, + SF_TOCO, + SF_TOCT, + SF_TOCN, + SF_E, + SF_NOTREADY = 0xF +} status_format; + +typedef enum { + CMD_NOP, + CMD_STOP, + CMD_REPORT_REQUEST, + CMD_READ, + CMD_SEEK, + CMD_INVALID, + CMD_PAUSE, + CMD_PLAY, + CMD_FFWD, + CMD_RWD, + CMD_TRACK_SKIP, + CMD_TRACK_CUE, + CMD_DOOR_CLOSE, + CMD_DOOR_OPEN +} host_cmd; + +typedef enum { + DS_STOP, + DS_PLAY, + DS_SEEK, + DS_SCAN, + DS_PAUSE, + DS_DOOR_OPEN, + DS_SUM_ERROR, + DS_CMD_ERROR, + DS_FUNC_ERROR, + DS_TOC_READ, + DS_TRACKING, + DS_NO_DISC, + DS_DISC_LEADOUT, + DS_DISC_LEADIN, + DS_TRAY_MOVING, +} drive_status; + + +typedef struct { + uint8_t status; + uint8_t format; + union { + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + uint8_t flags; + } time; + struct { + uint8_t track_high; + uint8_t track_low; + uint8_t padding0; + uint8_t padding1; + uint8_t control; + uint8_t adr; + uint8_t flags; + } track; + struct { + uint8_t first_track_high; + uint8_t first_track_low; + uint8_t last_track_high; + uint8_t last_track_low; + uint8_t version; + uint8_t padding; + uint8_t flags; + } toct; + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + uint8_t track_low; + } tocn; + struct { + } error; + } b; + uint8_t checksum; +} cdd_status; + +typedef struct { + uint8_t cmd_type; + uint8_t must_be_zero; + union { + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + } time; + struct { + uint8_t padding; + uint8_t status_type; + uint8_t track_high; + uint8_t track_low; + } format; + struct { + uint8_t padding; + uint8_t direction; + uint8_t tracks_highest; + uint8_t tracks_midhigh; + uint8_t tracks_midlow; + uint8_t tracks_lowest; + } skip; + struct { + uint8_t track_high; + uint8_t track_low; + } track; + } b; + uint8_t padding; + uint8_t checksum; +} cdd_cmd; + +typedef struct { + system_media *media; + uint32_t cycle; //this is in CD block CLKS + uint32_t next_int_cycle; //this is in SCD MCLKS + uint32_t last_subcode_cycle; + uint32_t last_nibble_cycle; + uint32_t last_byte_cycle; + int current_status_nibble; + int current_cmd_nibble; + int current_sector_byte; + uint32_t head_pba; + uint32_t seek_pba; + cdd_status status_buffer; + cdd_cmd cmd_buffer; + status_format requested_format; + drive_status status; + drive_status error_status; + uint8_t requested_track; + uint8_t cmd_recv_wait; + uint8_t cmd_recv_pending; + uint8_t int_pending; + uint8_t toc_valid; + uint8_t first_cmd_received; + uint8_t seeking; + uint8_t in_fake_pregap; +} cdd_mcu; + +void cdd_mcu_init(cdd_mcu *context, system_media *media); +void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array, lc8951* cdc); +void cdd_hock_enabled(cdd_mcu *context); +void cdd_hock_disabled(cdd_mcu *context); +void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array); +void cdd_mcu_adjust_cycle(cdd_mcu *context, uint32_t deduction); + +#endif //CD_MCU_H_ diff -r 3748a2a8a4b7 -r cc13c100b027 cue.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cue.c Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,159 @@ +#include + +#include "system.h" +#include "util.h" + +static char* cmd_start(char *cur) +{ + while (*cur && isblank(*cur)) + { + cur++; + } + return cur; +} + +static char* next_line(char *cur) +{ + while (*cur && *cur != '\n') + { + cur++; + } + if (*cur) { + return cur + 1; + } + return NULL; +} + +static uint32_t timecode_to_lba(char *timecode) +{ + char *end; + int seconds = 0, frames = 0; + int minutes = strtol(timecode, &end, 10); + if (end) { + timecode = end + 1; + seconds = strtol(timecode, &end, 10); + if (end) { + timecode = end + 1; + frames = strtol(timecode, NULL, 10); + } + } + seconds += minutes * 60; + return seconds * 75 + frames; + +} + +uint8_t parse_cue(system_media *media) +{ + char *line = media->buffer; + media->num_tracks = 0; + do { + char *cmd = cmd_start(line); + if (cmd) { + if (startswith(cmd, "TRACK ")) { + media->num_tracks++; + } + line = next_line(cmd); + } else { + line = NULL; + } + } while (line); + track_info *tracks = calloc(sizeof(track_info), media->num_tracks); + media->tracks = tracks; + line = media->buffer; + int track = -1; + do { + char *cmd = cmd_start(line); + if (cmd) { + if (startswith(cmd, "TRACK ")) { + track++; + cmd += 6; + char *end; + int file_track = strtol(cmd, &end, 10); + if (file_track != (track + 1)) { + warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); + } + cmd = cmd_start(end); + if (cmd) { + tracks[track].type = startswith(cmd, "AUDIO") ? TRACK_AUDIO : TRACK_DATA; + } + } else if (startswith(cmd, "FILE ")) { + if (media->f) { + warning("CUE sheets with multiple FILE commands are not supported\n"); + } else { + cmd += 5; + cmd = strchr(cmd, '"'); + if (cmd) { + cmd++; + char *end = strchr(cmd, '"'); + if (end) { + char *fname; + //TODO: zipped BIN/CUE support + if (is_absolute_path(cmd)) { + fname = malloc(end-cmd + 1); + memcpy(fname, cmd, end-cmd); + fname[end-cmd] = 0; + } else { + size_t dirlen = strlen(media->dir); + fname = malloc(dirlen + 1 + (end-cmd) + 1); + memcpy(fname, media->dir, dirlen); + fname[dirlen] = PATH_SEP[0]; + memcpy(fname + dirlen + 1, cmd, end-cmd); + fname[dirlen + 1 + (end-cmd)] = 0; + } + media->f = fopen(fname, "rb"); + if (!media->f) { + fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); + } + free(fname); + } + } + } + } else if (track >= 0) { + if (startswith(cmd, "PREGAP ")) { + tracks[track].fake_pregap = timecode_to_lba(cmd + 7); + } else if (startswith(cmd, "INDEX ")) { + char *after; + int index = strtol(cmd + 6, &after, 10); + if (!index) { + tracks[track].pregap_lba = timecode_to_lba(after); + } else if (index == 1) { + tracks[track].start_lba = timecode_to_lba(after); + } + } + } + if (cmd) { + line = next_line(cmd); + } else { + line = NULL; + } + } else { + line = NULL; + } + } while (line); + for (uint32_t i = 0; i < (media->num_tracks - 1); i++) + { + uint32_t next = i + 1; + tracks[i].end_lba = tracks[next].pregap_lba ? tracks[next].pregap_lba : tracks[next].start_lba; + } + if (media->f) { + //end of last track is implicitly defined by file size + tracks[media->num_tracks-1].end_lba = file_size(media->f) / 2352; + //replace cue sheet with first sector + free(media->buffer); + media->buffer = calloc(2048, 1); + if (tracks[0].type = TRACK_DATA) { + // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector + uint8_t msf[3]; + fseek(media->f, 12, SEEK_SET); + if (sizeof(msf) == fread(msf, 1, sizeof(msf), media->f)) { + tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; + } + } + + fseek(media->f, 16, SEEK_SET); + media->size = fread(media->buffer, 1, 2048, media->f); + } + uint8_t valid = tracks > 0 && media->f != NULL; + media->type = valid ? MEDIA_CDROM : MEDIA_CART; + return valid; +} diff -r 3748a2a8a4b7 -r cc13c100b027 cue.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cue.h Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,6 @@ +#ifndef CUE_H_ +#define CUE_H_ + +uint8_t parse_cue(system_media *media); + +#endif //CUE_H_ diff -r 3748a2a8a4b7 -r cc13c100b027 debug.c --- a/debug.c Sat Jan 01 18:54:46 2022 -0800 +++ b/debug.c Sun Jan 30 22:29:29 2022 -0800 @@ -740,7 +740,7 @@ m68k_disasm(&inst, input_buf); printf("%X: %s\n", address, input_buf); } - + break; case 'n': if (inst.op == M68K_RTS) { @@ -1012,7 +1012,7 @@ init_terminal(); - sync_components(context, 0); + context->options->sync_components(context, 0); genesis_context *gen = context->system; vdp_force_update_framebuffer(gen->vdp); //probably not necessary, but let's play it safe diff -r 3748a2a8a4b7 -r cc13c100b027 genesis.c --- a/genesis.c Sat Jan 01 18:54:46 2022 -0800 +++ b/genesis.c Sun Jan 30 22:29:29 2022 -0800 @@ -4,6 +4,7 @@ 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 "segacd.h" #include "blastem.h" #include "nor.h" #include @@ -246,13 +247,22 @@ uint16_t read_dma_value(uint32_t address) { genesis_context *genesis = (genesis_context *)current_system; + address *= 2; //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area if ((address >= 0xA00000 && address < 0xB00000) || (address >= 0xC00000 && address <= 0xE00000)) { return 0; } + if (genesis->expansion) { + segacd_context *cd = genesis->expansion; + uint32_t word_ram = cd->base + 0x200000; + uint32_t word_ram_end = cd->base + 0x240000; + if (address >= word_ram && address < word_ram_end) { + //FIXME: first word should just be garbage + address -= 2; + } + } - //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do multiply by 2 - return read_word(address * 2, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen, genesis->m68k); + return read_word(address, (void **)genesis->m68k->mem_pointers, &genesis->m68k->options->gen, genesis->m68k); } static uint16_t get_open_bus_value(system_header *system) @@ -412,7 +422,7 @@ #define ADJUST_BUFFER (8*MCLKS_LINE*313) #define MAX_NO_ADJUST (UINT_MAX-ADJUST_BUFFER) -m68k_context * sync_components(m68k_context * context, uint32_t address) +static m68k_context *sync_components(m68k_context * context, uint32_t address) { genesis_context * gen = context->system; vdp_context * v_context = gen->vdp; @@ -433,6 +443,9 @@ io_run(gen->io.ports, mclks); io_run(gen->io.ports + 1, mclks); io_run(gen->io.ports + 2, mclks); + if (gen->expansion) { + scd_run(gen->expansion, gen_cycle_to_scd(mclks, gen)); + } if (mclks >= gen->reset_cycle) { gen->reset_requested = 1; context->should_return = 1; @@ -470,6 +483,9 @@ gen->reset_cycle -= deduction; } event_cycle_adjust(mclks, deduction); + if (gen->expansion) { + scd_adjust_cycle(gen->expansion, deduction); + } gen->last_flush_cycle -= deduction; } } else if (mclks - gen->last_flush_cycle > gen->soft_flush_cycles) { @@ -1410,7 +1426,8 @@ } else { if (gen->header.enter_debugger) { gen->header.enter_debugger = 0; - uint32_t address = gen->cart[2] << 16 | gen->cart[3]; + uint32_t address = read_word(4, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k) << 16 + | read_word(6, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k); insert_breakpoint(gen->m68k, address, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter); } m68k_reset(gen->m68k); @@ -1747,7 +1764,7 @@ return context; } -genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, uint32_t system_opts, uint8_t force_region) +static genesis_context *shared_init(uint32_t system_opts, rom_info *rom, 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 }, @@ -1756,6 +1773,16 @@ { 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} }; + + char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0", TVAL_PTR).ptrval; + if (!m68k_divider) { + m68k_divider = "7"; + } + MCLKS_PER_68K = atoi(m68k_divider); + if (!MCLKS_PER_68K) { + MCLKS_PER_68K = 7; + } + genesis_context *gen = calloc(1, sizeof(genesis_context)); gen->header.set_speed_percent = set_speed_percent; gen->header.start_context = start_genesis; @@ -1831,10 +1858,8 @@ gen->z80->system = gen; gen->z80->mem_pointers[0] = gen->zram; - gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)main_rom; + gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = NULL; - gen->cart = main_rom; - gen->lock_on = lock_on; gen->work_ram = calloc(2, RAM_WORDS); if (!strcmp("random", tern_find_path_default(config, "system\0ram_init\0", (tern_val){.ptrval = "zero"}, TVAL_PTR).ptrval)) { @@ -1864,45 +1889,92 @@ gen->vdp->vsram[i] = rand(); } } - setup_io_devices(config, rom, &gen->io); - gen->header.has_keyboard = io_has_keyboard(&gen->io); + + return gen; +} + +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}, + {0x000000, 0xFFFFFF, 0xFFFFFF, 0, 0, 0, NULL, + (read_16_fun)unused_read, (write_16_fun)unused_write, + (read_8_fun)unused_read_b, (write_8_fun)unused_write_b} +}; +const size_t base_chunks = sizeof(base_map)/sizeof(*base_map); - gen->mapper_type = rom->mapper_type; - gen->save_type = rom->save_type; +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) +{ + tern_node *rom_db = get_rom_db(); + rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, base_chunks); + rom = info.rom; + rom_size = info.rom_size; +#ifndef BLASTEM_BIG_ENDIAN + byteswap_rom(rom_size, rom); + if (lock_on) { + byteswap_rom(lock_on_size, lock_on); + } +#endif + char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0", TVAL_PTR).ptrval; + if (!m68k_divider) { + m68k_divider = "7"; + } + MCLKS_PER_68K = atoi(m68k_divider); + if (!MCLKS_PER_68K) { + MCLKS_PER_68K = 7; + } + genesis_context *gen = shared_init(ym_opts, &info, force_region); + gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = rom; + + gen->cart = rom; + gen->lock_on = lock_on; + + setup_io_devices(config, &info, &gen->io); + gen->header.has_keyboard = io_has_keyboard(&gen->io); + gen->mapper_type = info.mapper_type; + gen->save_type = info.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; + gen->save_ram_mask = info.save_mask; + gen->save_size = info.save_size; + gen->save_storage = info.save_buffer; + gen->eeprom_map = info.eeprom_map; + gen->num_eeprom = info.num_eeprom; if (gen->save_type == SAVE_I2C) { eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size); } else if (gen->save_type == SAVE_NOR) { - memcpy(&gen->nor, rom->nor, sizeof(gen->nor)); - //nor_flash_init(&gen->nor, gen->save_storage, gen->save_size, rom->save_page_size, rom->save_product_id, rom->save_bus); + memcpy(&gen->nor, info.nor, sizeof(gen->nor)); + //nor_flash_init(&gen->nor, gen->save_storage, gen->save_size, info.save_page_size, info.save_product_id, info.save_bus); } } else { gen->save_storage = NULL; } - gen->mapper_start_index = rom->mapper_start_index; + gen->mapper_start_index = info.mapper_start_index; + + tern_node *model = get_model(config, SYSTEM_GENESIS); + uint8_t tmss = !strcmp(tern_find_ptr_default(model, "tmss", "off"), "on"); //This must happen before we generate memory access functions in init_m68k_opts uint8_t next_ptr_index = 0; uint32_t tmss_min_alloc = 16 * 1024; - for (int i = 0; i < rom->map_chunks; i++) + for (int i = 0; i < info.map_chunks; i++) { - if (rom->map[i].start == 0xE00000) { - rom->map[i].buffer = gen->work_ram; + if (info.map[i].start == 0xE00000) { + info.map[i].buffer = gen->work_ram; if (!tmss) { break; } } - if (rom->map[i].flags & MMAP_PTR_IDX && rom->map[i].ptr_index >= next_ptr_index) { - next_ptr_index = rom->map[i].ptr_index + 1; + if (info.map[i].flags & MMAP_PTR_IDX && info.map[i].ptr_index >= next_ptr_index) { + next_ptr_index = info.map[i].ptr_index + 1; } - if (rom->map[i].start < 0x400000 && rom->map[i].read_16 != unused_read) { - uint32_t highest_offset = (rom->map[i].end & rom->map[i].mask) + 1; + if (info.map[i].start < 0x400000 && info.map[i].read_16 != unused_read) { + uint32_t highest_offset = (info.map[i].end & info.map[i].mask) + 1; if (highest_offset > tmss_min_alloc) { tmss_min_alloc = highest_offset; } @@ -1941,105 +2013,136 @@ } //modify mappings for ROM space to point to the TMSS ROM and fixup flags to allow switching back and forth //WARNING: This code makes some pretty big assumptions about the kinds of map chunks it will encounter - for (int i = 0; i < rom->map_chunks; i++) + for (int i = 0; i < info.map_chunks; i++) { - if (rom->map[i].start < 0x400000 && rom->map[i].read_16 != unused_read) { - if (rom->map[i].flags == MMAP_READ) { + if (info.map[i].start < 0x400000 && info.map[i].read_16 != unused_read) { + if (info.map[i].flags == MMAP_READ) { //Normal ROM - rom->map[i].flags |= MMAP_PTR_IDX | MMAP_CODE; - rom->map[i].ptr_index = next_ptr_index++; - if (rom->map[i].ptr_index >= NUM_MEM_AREAS) { + info.map[i].flags |= MMAP_PTR_IDX | MMAP_CODE; + info.map[i].ptr_index = next_ptr_index++; + if (info.map[i].ptr_index >= NUM_MEM_AREAS) { fatal_error("Too many memmap chunks with MMAP_PTR_IDX after TMSS remap\n"); } - gen->tmss_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; - rom->map[i].buffer = buffer + (rom->map[i].start & ~rom->map[i].mask & (tmss_size - 1)); - } else if (rom->map[i].flags & MMAP_PTR_IDX) { + gen->tmss_pointers[info.map[i].ptr_index] = info.map[i].buffer; + info.map[i].buffer = buffer + (info.map[i].start & ~info.map[i].mask & (tmss_size - 1)); + } else if (info.map[i].flags & MMAP_PTR_IDX) { //Sega mapper page or multi-game mapper - gen->tmss_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; - rom->map[i].buffer = buffer + (rom->map[i].start & ~rom->map[i].mask & (tmss_size - 1)); - if (rom->map[i].write_16) { + gen->tmss_pointers[info.map[i].ptr_index] = info.map[i].buffer; + info.map[i].buffer = buffer + (info.map[i].start & ~info.map[i].mask & (tmss_size - 1)); + if (info.map[i].write_16) { if (!gen->tmss_write_16) { - gen->tmss_write_16 = rom->map[i].write_16; - gen->tmss_write_8 = rom->map[i].write_8; - rom->map[i].write_16 = tmss_rom_write_16; - rom->map[i].write_8 = tmss_rom_write_8; - } else if (gen->tmss_write_16 == rom->map[i].write_16) { - rom->map[i].write_16 = tmss_rom_write_16; - rom->map[i].write_8 = tmss_rom_write_8; + gen->tmss_write_16 = info.map[i].write_16; + gen->tmss_write_8 = info.map[i].write_8; + info.map[i].write_16 = tmss_rom_write_16; + info.map[i].write_8 = tmss_rom_write_8; + } else if (gen->tmss_write_16 == info.map[i].write_16) { + info.map[i].write_16 = tmss_rom_write_16; + info.map[i].write_8 = tmss_rom_write_8; } else { - warning("Chunk starting at %X has a write function, but we've already stored a different one for TMSS remap\n", rom->map[i].start); + warning("Chunk starting at %X has a write function, but we've already stored a different one for TMSS remap\n", info.map[i].start); } } - } else if ((rom->map[i].flags & (MMAP_READ | MMAP_WRITE)) == (MMAP_READ | MMAP_WRITE)) { + } else if ((info.map[i].flags & (MMAP_READ | MMAP_WRITE)) == (MMAP_READ | MMAP_WRITE)) { //RAM or SRAM - rom->map[i].flags |= MMAP_PTR_IDX; - rom->map[i].ptr_index = next_ptr_index++; - gen->tmss_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; - rom->map[i].buffer = buffer + (rom->map[i].start & ~rom->map[i].mask & (tmss_size - 1)); - if (!gen->tmss_write_offset || gen->tmss_write_offset == rom->map[i].start) { - gen->tmss_write_offset = rom->map[i].start; - rom->map[i].flags &= ~MMAP_WRITE; - if (rom->map[i].flags & MMAP_ONLY_ODD) { - rom->map[i].write_16 = tmss_odd_write_16; - rom->map[i].write_8 = tmss_odd_write_8; - } else if (rom->map[i].flags & MMAP_ONLY_EVEN) { - rom->map[i].write_16 = tmss_even_write_16; - rom->map[i].write_8 = tmss_even_write_8; + info.map[i].flags |= MMAP_PTR_IDX; + info.map[i].ptr_index = next_ptr_index++; + gen->tmss_pointers[info.map[i].ptr_index] = info.map[i].buffer; + info.map[i].buffer = buffer + (info.map[i].start & ~info.map[i].mask & (tmss_size - 1)); + if (!gen->tmss_write_offset || gen->tmss_write_offset == info.map[i].start) { + gen->tmss_write_offset = info.map[i].start; + info.map[i].flags &= ~MMAP_WRITE; + if (info.map[i].flags & MMAP_ONLY_ODD) { + info.map[i].write_16 = tmss_odd_write_16; + info.map[i].write_8 = tmss_odd_write_8; + } else if (info.map[i].flags & MMAP_ONLY_EVEN) { + info.map[i].write_16 = tmss_even_write_16; + info.map[i].write_8 = tmss_even_write_8; } else { - rom->map[i].write_16 = tmss_word_write_16; - rom->map[i].write_8 = tmss_word_write_8; + info.map[i].write_16 = tmss_word_write_16; + info.map[i].write_8 = tmss_word_write_8; } } else { - warning("Could not remap writes for chunk starting at %X for TMSS because write_offset is %X\n", rom->map[i].start, gen->tmss_write_offset); + warning("Could not remap writes for chunk starting at %X for TMSS because write_offset is %X\n", info.map[i].start, gen->tmss_write_offset); } - } else if (rom->map[i].flags & MMAP_READ_CODE) { + } else if (info.map[i].flags & MMAP_READ_CODE) { //NOR flash - rom->map[i].flags |= MMAP_PTR_IDX; - rom->map[i].ptr_index = next_ptr_index++; - if (rom->map[i].ptr_index >= NUM_MEM_AREAS) { + info.map[i].flags |= MMAP_PTR_IDX; + info.map[i].ptr_index = next_ptr_index++; + if (info.map[i].ptr_index >= NUM_MEM_AREAS) { fatal_error("Too many memmap chunks with MMAP_PTR_IDX after TMSS remap\n"); } - gen->tmss_pointers[rom->map[i].ptr_index] = rom->map[i].buffer; - rom->map[i].buffer = buffer + (rom->map[i].start & ~rom->map[i].mask & (tmss_size - 1)); + gen->tmss_pointers[info.map[i].ptr_index] = info.map[i].buffer; + info.map[i].buffer = buffer + (info.map[i].start & ~info.map[i].mask & (tmss_size - 1)); if (!gen->tmss_write_16) { - gen->tmss_write_16 = rom->map[i].write_16; - gen->tmss_write_8 = rom->map[i].write_8; - gen->tmss_read_16 = rom->map[i].read_16; - gen->tmss_read_8 = rom->map[i].read_8; - rom->map[i].write_16 = tmss_rom_write_16; - rom->map[i].write_8 = tmss_rom_write_8; - rom->map[i].read_16 = tmss_rom_read_16; - rom->map[i].read_8 = tmss_rom_read_8; - } else if (gen->tmss_write_16 == rom->map[i].write_16) { - rom->map[i].write_16 = tmss_rom_write_16; - rom->map[i].write_8 = tmss_rom_write_8; - rom->map[i].read_16 = tmss_rom_read_16; - rom->map[i].read_8 = tmss_rom_read_8; + gen->tmss_write_16 = info.map[i].write_16; + gen->tmss_write_8 = info.map[i].write_8; + gen->tmss_read_16 = info.map[i].read_16; + gen->tmss_read_8 = info.map[i].read_8; + info.map[i].write_16 = tmss_rom_write_16; + info.map[i].write_8 = tmss_rom_write_8; + info.map[i].read_16 = tmss_rom_read_16; + info.map[i].read_8 = tmss_rom_read_8; + } else if (gen->tmss_write_16 == info.map[i].write_16) { + info.map[i].write_16 = tmss_rom_write_16; + info.map[i].write_8 = tmss_rom_write_8; + info.map[i].read_16 = tmss_rom_read_16; + info.map[i].read_8 = tmss_rom_read_8; } else { - warning("Chunk starting at %X has a write function, but we've already stored a different one for TMSS remap\n", rom->map[i].start); + warning("Chunk starting at %X has a write function, but we've already stored a different one for TMSS remap\n", info.map[i].start); } } else { - warning("Didn't remap chunk starting at %X for TMSS because it has flags %X\n", rom->map[i].start, rom->map[i].flags); + warning("Didn't remap chunk starting at %X for TMSS because it has flags %X\n", info.map[i].start, info.map[i].flags); } } } gen->tmss_buffer = buffer; } + memmap_chunk* map = info.map; + uint32_t map_chunks = info.map_chunks; + if (info.wants_cd) { + segacd_context *cd = alloc_configure_segacd((system_media *)current_media(), 0, force_region, &info); + gen->expansion = cd; + gen->version_reg &= ~NO_DISK; + cd->genesis = gen; + uint32_t cd_chunks; + memmap_chunk *cd_map = segacd_main_cpu_map(gen->expansion, 1, &cd_chunks); + map_chunks = cd_chunks + info.map_chunks; + map = calloc(map_chunks, sizeof(memmap_chunk)); + memcpy(map, info.map, sizeof(memmap_chunk) * (info.map_chunks - 1)); + memcpy(map + info.map_chunks - 1, cd_map, sizeof(memmap_chunk) * cd_chunks); + memcpy(map + map_chunks - 1, info.map + info.map_chunks - 1, sizeof(memmap_chunk)); + free(info.map); + int max_ptr_index = -1; + for (int i = 0; i < info.map_chunks - 1; i++) + { + if (map[i].flags & MMAP_PTR_IDX && map[i].ptr_index > max_ptr_index) { + max_ptr_index = map[i].ptr_index; + } + } + cd->memptr_start_index + max_ptr_index + 1; + for (int i = info.map_chunks - 1; i < map_chunks - 1; i++) + { + if (map[i].flags & MMAP_PTR_IDX) { + map[i].ptr_index += cd->memptr_start_index; + } + } + cd->base = 0x400000; + } m68k_options *opts = malloc(sizeof(m68k_options)); - init_m68k_opts(opts, rom->map, rom->map_chunks, MCLKS_PER_68K); + init_m68k_opts(opts, map, map_chunks, MCLKS_PER_68K, sync_components); if (!strcmp(tern_find_ptr_default(model, "tas", "broken"), "broken")) { opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; } gen->m68k = init_68k_context(opts, NULL); gen->m68k->system = gen; - opts->address_log = (system_opts & OPT_ADDRESS_LOG) ? fopen("address.log", "w") : NULL; + opts->address_log = (ym_opts & OPT_ADDRESS_LOG) ? fopen("address.log", "w") : NULL; //This must happen after the 68K context has been allocated - for (int i = 0; i < rom->map_chunks; i++) + for (int i = 0; i < 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 (map[i].flags & MMAP_PTR_IDX) { + gen->m68k->mem_pointers[map[i].ptr_index] = map[i].buffer; } } @@ -2055,41 +2158,45 @@ return gen; } -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) +genesis_context *alloc_config_genesis_cdboot(system_media *media, uint32_t system_opts, uint8_t force_region) { - 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}, - {0x000000, 0xFFFFFF, 0xFFFFFF, 0, 0, 0, NULL, - (read_16_fun)unused_read, (write_16_fun)unused_write, - (read_8_fun)unused_read_b, (write_8_fun)unused_write_b} - }; - static tern_node *rom_db; - if (!rom_db) { - rom_db = load_rom_db(); + tern_node *rom_db = get_rom_db(); + rom_info info = configure_rom(rom_db, media->buffer, media->size, NULL, 0, base_map, base_chunks); + + segacd_context *cd = alloc_configure_segacd(media, system_opts, force_region, &info); + genesis_context *gen = shared_init(system_opts, &info, force_region); + gen->cart = gen->lock_on = NULL; + gen->save_storage = NULL; + gen->save_type = SAVE_NONE; + gen->version_reg &= ~NO_DISK; + + gen->expansion = cd; + gen->version_reg &= ~NO_DISK; + cd->genesis = gen; + setup_io_devices(config, &info, &gen->io); + + uint32_t cd_chunks; + memmap_chunk *cd_map = segacd_main_cpu_map(gen->expansion, 0, &cd_chunks); + memmap_chunk *map = malloc(sizeof(memmap_chunk) * (cd_chunks + base_chunks)); + memcpy(map, cd_map, sizeof(memmap_chunk) * cd_chunks); + memcpy(map + cd_chunks, base_map, sizeof(memmap_chunk) * base_chunks); + map[cd_chunks].buffer = gen->work_ram; + uint32_t num_chunks = cd_chunks + base_chunks; + + m68k_options *opts = malloc(sizeof(m68k_options)); + init_m68k_opts(opts, map, num_chunks, MCLKS_PER_68K, sync_components); + //TODO: make this configurable + opts->gen.flags |= M68K_OPT_BROKEN_READ_MODIFY; + gen->m68k = init_68k_context(opts, NULL); + gen->m68k->system = gen; + opts->address_log = (system_opts & OPT_ADDRESS_LOG) ? fopen("address.log", "w") : NULL; + + //This must happen after the 68K context has been allocated + for (int i = 0; i < num_chunks; i++) + { + if (map[i].flags & MMAP_PTR_IDX) { + gen->m68k->mem_pointers[map[i].ptr_index] = map[i].buffer; + } } - rom_info info = configure_rom(rom_db, rom, rom_size, lock_on, lock_on_size, base_map, sizeof(base_map)/sizeof(base_map[0])); - rom = info.rom; - rom_size = info.rom_size; -#ifndef BLASTEM_BIG_ENDIAN - byteswap_rom(rom_size, rom); - if (lock_on) { - byteswap_rom(lock_on_size, lock_on); - } -#endif - char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0", TVAL_PTR).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, rom, lock_on, ym_opts, force_region); + return gen; } diff -r 3748a2a8a4b7 -r cc13c100b027 genesis.h --- a/genesis.h Sat Jan 01 18:54:46 2022 -0800 +++ b/genesis.h Sun Jan 30 22:29:29 2022 -0800 @@ -35,6 +35,7 @@ uint16_t *lock_on; uint16_t *work_ram; uint8_t *zram; + void *expansion; void *extra; uint8_t *save_storage; void *mapper_temp; @@ -80,8 +81,8 @@ #define RAM_WORDS 32 * 1024 #define Z80_RAM_BYTES 8 * 1024 -m68k_context * sync_components(m68k_context *context, uint32_t address); 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); 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); diff -r 3748a2a8a4b7 -r cc13c100b027 lc8951.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lc8951.c Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,326 @@ +#include "lc8951.h" +#include "backend.h" + +enum { + COMIN, + IFSTAT, + DBCL, + DBCH, + HEAD0, + HEAD1, + HEAD2, + HEAD3, + PTL, + PTH, + WAL, + WAH, + STAT0, + STAT1, + STAT2, + STAT3, + + SBOUT = COMIN, + IFCTRL = IFSTAT, + DACL = HEAD0, + DACH = HEAD1, + DTTRG = HEAD2, + DTACK = HEAD3, + WAL_WRITE = PTL, + WAH_WRITE = PTH, + CTRL0 = WAL, + CTRL1 = WAH, + PTL_WRITE = STAT0, + PTH_WRITE = STAT1, + RESET = STAT3 +}; + +//IFCTRL +#define BIT_CMDIEN 0x80 +#define BIT_DTEIEN 0x40 +#define BIT_DECIEN 0x20 +#define BIT_CMDBK 0x10 +#define BIT_DTWAI 0x08 +#define BIT_STWAI 0x04 +#define BIT_DOUTEN 0x02 +#define BIT_SOUTEN 0x01 + +//IFSTAT +#define BIT_CMDI 0x80 +#define BIT_DTEI 0x40 +#define BIT_DECI 0x20 +#define BIT_DTBSY 0x08 +#define BIT_STBSY 0x04 +#define BIT_DTEN 0x02 +#define BIT_STEN 0x01 + +//CTRL0 +#define BIT_DECEN 0x80 +#define BIT_WRRQ 0x04 + +//STAT0 +#define BIT_CRCOK 0x80 + +//STAT3 +#define BIT_VALST 0x80 + +//datasheet timing info +//3 cycles for memory operation +//6 cycles min for DMA-mode host transfer + +void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data) +{ + //This seems to vary somewhat between Sega CD models + //unclear if the difference is in the lc8951 or gate array + context->regs[IFSTAT] = 0xFF; + context->ar_mask = 0x1F; + context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period + context->byte_handler = byte_handler; + context->handler_data = handler_data; + context->decode_end = CYCLE_NEVER; + context->transfer_end = CYCLE_NEVER; +} + +void lc8951_reg_write(lc8951 *context, uint8_t value) +{ + switch (context->ar) + { + case SBOUT: + context->regs[context->ar] = value; + if (context->ifctrl & BIT_SOUTEN) { + context->regs[IFSTAT] &= ~BIT_STBSY; + } + break; + case IFCTRL: + context->ifctrl = value; + if (!(value & BIT_SOUTEN)) { + context->regs[IFSTAT] |= BIT_STBSY; + } + if (!(value & BIT_DOUTEN)) { + context->regs[IFSTAT] |= BIT_DTBSY|BIT_DTEI; + context->transfer_end = CYCLE_NEVER; + } + break; + case DBCL: + context->regs[context->ar] = value; + break; + case DBCH: + context->regs[context->ar] = value & 0xF; + break; + case DACL: + context->dac &= 0xFF00; + context->dac |= value; + break; + case DACH: + context->dac &= 0xFF; + context->dac |= value << 8; + break; + case DTTRG: + if (context->ifctrl & BIT_DOUTEN) { + context->regs[IFSTAT] &= ~BIT_DTBSY; + uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); + context->transfer_end = context->cycle + transfer_size * context->clock_step; + printf("DTTRG: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); + } + break; + case DTACK: + context->regs[IFSTAT] |= BIT_DTEI; + break; + case WAL_WRITE: + context->regs[WAL] = value; + break; + case WAH_WRITE: + context->regs[WAH] = value; + break; + case CTRL0: + context->ctrl0 = value; + break; + case CTRL1: + context->ctrl1 = value; + break; + case PTL_WRITE: + context->regs[PTL] = value; + break; + case PTH_WRITE: + context->regs[PTH] = value; + //TODO: Datasheet says any write to PT triggers a decode, but initial tests suggest that's not the case + //Need to do more tests with other CTRL0/CTRL1 settings + //context->decode_end = context->cycle + 2352 * context->clock_step * 4; + break; + case RESET: + context->comin_count = 0; + context->regs[IFSTAT] = 0xFF; + break; + default: + break; + } + if (context->ar != SBOUT) { + context->ar++; + context->ar &= context->ar_mask; + } +} + +uint8_t lc8951_reg_read(lc8951 *context) +{ + uint8_t value; + if (context->ar == COMIN) { + if (!context->comin_count) { + return 0xFF; + } + value = context->comin[(context->comin_write - context->comin_count)&sizeof(context->comin)]; + context->comin_count--; + if (!context->comin_count) { + context->regs[IFSTAT] |= BIT_CMDI; + } + return value; + } + if (context->ar == STAT3) { + context->regs[IFSTAT] |= BIT_DECI; + } + if (context->ar >= sizeof(context->regs)) { + value = 0xFF; + } else { + value = context->regs[context->ar]; + } + printf("CDC read %X: %X\n", context->ar, value); + context->ar++; + context->ar &= context->ar_mask; + return value; +} + +void lc8951_ar_write(lc8951 *context, uint8_t value) +{ + context->ar = value & context->ar_mask; +} + +//25 MHz clock input (1/2 SCD MCLK) +//internal /2 divider +//3 cycles for each SRAM access (though might be crystal frequency rather than internal frequency) +//6 cycle period for DMA transfer out +// + +void lc8951_run(lc8951 *context, uint32_t cycle) +{ + for(; context->cycle < cycle; context->cycle += context->clock_step) + { + if (context->cycle >= context->decode_end) { + context->decode_end = CYCLE_NEVER; + context->regs[IFSTAT] &= ~BIT_DECI; + context->regs[STAT3] &= ~BIT_VALST; + if (context->ctrl0 & BIT_WRRQ) { + uint16_t block_start = (context->regs[PTL] | (context->regs[PTH] << 8)) & (sizeof(context->buffer)-1); + for (int reg = HEAD0; reg < PTL; reg++) + { + printf("Setting HEAD%d to buffer[%X]\n", reg - HEAD0, block_start); + context->regs[reg] =context->buffer[block_start++]; + block_start &= (sizeof(context->buffer)-1); + } + } + printf("Decode done %X:%X:%X mode %X\n", context->regs[HEAD0], context->regs[HEAD1], context->regs[HEAD2], context->regs[HEAD3]); + context->regs[STAT0] |= BIT_CRCOK; + } + if (context->transfer_end != CYCLE_NEVER) { + if (context->byte_handler(context->handler_data, context->buffer[context->dac & (sizeof(context->buffer)-1)])) { + context->dac++; + context->regs[DBCL]--; + if (context->regs[DBCL] == 0xFF) { + context->regs[DBCH]--; + if (context->regs[DBCH] == 0xFF) { + context->regs[IFSTAT] &= ~BIT_DTEI; + context->regs[IFSTAT] |= BIT_DTBSY; + if (context->cycle != context->transfer_end) { + printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle); + } + context->transfer_end = CYCLE_NEVER; + } + } + } else { + // pause transfer + context->transfer_end = CYCLE_NEVER; + } + } + } +} + +void lc8951_resume_transfer(lc8951 *context, uint32_t cycle) +{ + if (context->transfer_end == CYCLE_NEVER && (context->ifctrl & BIT_DOUTEN)) { + uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); + if (transfer_size != 0xFFFF) { + //HACK!!! Work around Sub CPU running longer than we would like and dragging other components with it + uint32_t step_diff = (context->cycle - cycle) / context->clock_step; + if (step_diff) { + context->cycle -= step_diff * context->clock_step; + } + context->transfer_end = context->cycle + transfer_size * context->clock_step; + printf("RESUME: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); + if (step_diff) { + lc8951_run(context, cycle); + } + } + } +} + +void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte) +{ + lc8951_run(context, cycle); + uint16_t current_write_addr = context->regs[WAL] | (context->regs[WAH] << 8); + if (sector_offset == 12) { + //we've recevied the sync pattern for the next block + + //header/status regs no longer considered "valid" + context->regs[STAT3] |= BIT_VALST; + //!DECI is set inactive at the same time as !VALST + context->regs[IFSTAT] |= BIT_DECI; + if (context->ctrl0 & BIT_DECEN) { + if (context->ctrl0 & BIT_WRRQ) { + uint16_t block_start = current_write_addr - 2352; + context->regs[PTL] = block_start; + context->regs[PTH] = block_start >> 8; + } + printf("Decoding block starting at %X\n", context->regs[PTL] | (context->regs[PTH] << 8)); + //TODO: Datasheet has some hints about how long decoding takes in the form of how long DECI is asserted + context->decode_end = context->cycle + 2352 * context->clock_step * 4; + } + } + if (sector_offset >= 12 && sector_offset < 16) { + //TODO: Handle SHDREN = 1 + if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN)) { + //monitor only mode + context->regs[HEAD0 + sector_offset - 12] = byte; + } + } + if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) { + context->buffer[current_write_addr & (sizeof(context->buffer)-1)] = byte; + context->regs[WAL]++; + if (!context->regs[WAL]) { + context->regs[WAH]++; + } + } +} + +uint32_t lc8951_next_interrupt(lc8951 *context) +{ + if ((~context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { + //interrupt already pending + return context->cycle; + } + uint32_t deci_cycle = CYCLE_NEVER; + if (context->ifctrl & BIT_DECI) { + deci_cycle = context->decode_end; + } + uint32_t dtei_cycle = CYCLE_NEVER; + if (context->ifctrl & BIT_DTEI) { + dtei_cycle = context->transfer_end; + } + return deci_cycle < dtei_cycle ? deci_cycle : dtei_cycle; +} + +void lc8951_adjust_cycles(lc8951 *context, uint32_t deduction) +{ + if (context->decode_end != CYCLE_NEVER) { + context->decode_end -= deduction; + } + if (context->transfer_end != CYCLE_NEVER) { + context->transfer_end -= deduction; + } +} diff -r 3748a2a8a4b7 -r cc13c100b027 lc8951.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lc8951.h Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,41 @@ +#ifndef LC8951_H_ +#define LC8951_H_ + +#include + +typedef uint8_t (*lcd8951_byte_recv_fun)(void *data, uint8_t byte); + +typedef struct { + lcd8951_byte_recv_fun byte_handler; + void *handler_data; + uint32_t cycle; + uint32_t clock_step; + uint32_t decode_end; + uint32_t transfer_end; + + uint8_t buffer[0x4000]; + + uint8_t regs[16]; + uint8_t comin[8]; + + uint16_t dac; + uint8_t comin_write; + uint8_t comin_count; + uint8_t ifctrl; + uint8_t ctrl0; + uint8_t ctrl1; + uint8_t ar; + uint8_t ar_mask; +} lc8951; + +void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data); +void lc8951_run(lc8951 *context, uint32_t cycle); +void lc8951_reg_write(lc8951 *context, uint8_t value); +uint8_t lc8951_reg_read(lc8951 *context); +void lc8951_ar_write(lc8951 *context, uint8_t value); +void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte); +uint32_t lc8951_next_interrupt(lc8951 *context); +void lc8951_resume_transfer(lc8951 *context, uint32_t cycle); +void lc8951_adjust_cycles(lc8951 *context, uint32_t deduction); + +#endif //LC8951_H_ diff -r 3748a2a8a4b7 -r cc13c100b027 m68k_core.c --- a/m68k_core.c Sat Jan 01 18:54:46 2022 -0800 +++ b/m68k_core.c Sun Jan 30 22:29:29 2022 -0800 @@ -94,7 +94,7 @@ void m68k_save_result(m68kinst * inst, m68k_options * opts) { if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG && inst->dst.addr_mode != MODE_UNUSED) { - if (inst->dst.addr_mode == MODE_AREG_PREDEC && + if (inst->dst.addr_mode == MODE_AREG_PREDEC && ((inst->src.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) || (inst->op == M68K_NBCD)) ) { areg_to_native(opts, inst->dst.params.regs.pri, opts->gen.scratch2); @@ -489,7 +489,7 @@ } opts->extra_code = opts->gen.code; opts->gen.code = tmp; - + rts(&opts->extra_code); return impl; } @@ -499,7 +499,7 @@ code_info *code = &opts->gen.code; uint8_t early_cycles; uint16_t num_regs = inst->src.addr_mode == MODE_REG ? inst->src.params.immed : inst->dst.params.immed; - { + { //TODO: Move this popcount alg to a utility function uint16_t a = (num_regs & 0b1010101010101010) >> 1; uint16_t b = num_regs & 0b0101010101010101; @@ -549,7 +549,7 @@ m68k_disasm(inst, disasm_buf); fatal_error("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode); } - + cycles(&opts->gen, early_cycles); if (num_regs <= 9) { translate_movem_regtomem_reglist(opts, inst); @@ -596,7 +596,7 @@ fatal_error("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode); } cycles(&opts->gen, early_cycles); - + if (num_regs <= 9) { translate_movem_memtoreg_reglist(opts, inst); } else { @@ -625,7 +625,7 @@ static void translate_m68k_rte(m68k_options *opts, m68kinst *inst) { m68k_trap_if_not_supervisor(opts, inst); - + code_info *code = &opts->gen.code; //Read saved SR areg_to_native(opts, 7, opts->gen.scratch1); @@ -646,7 +646,7 @@ code_ptr get_native_address(m68k_options *opts, uint32_t address) { native_map_slot * native_code_map = opts->gen.native_code_map; - + memmap_chunk const *mem_chunk = find_map_chunk(address, &opts->gen, 0, NULL); if (mem_chunk) { //calculate the lowest alias for this address @@ -680,7 +680,7 @@ } else { address &= opts->gen.address_mask; } - + uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { return 0; @@ -728,7 +728,7 @@ } else { address &= opts->gen.address_mask; } - + uint32_t chunk = address / NATIVE_CHUNK_SIZE; if (!native_code_map[chunk].base) { native_code_map[chunk].base = native_addr; @@ -830,7 +830,7 @@ warning("Spurious breakpoing at %X\n", address); remove_breakpoint(context, address); } - + return context; } @@ -958,15 +958,15 @@ } code_ptr start = opts->gen.code.cur; check_cycles_int(&opts->gen, inst->address); - + m68k_debug_handler bp; if ((bp = find_breakpoint(context, inst->address))) { m68k_breakpoint_patch(context, inst->address, bp, start); } - - //log_address(&opts->gen, inst->address, "M68K: %X @ %d\n"); + + //log_address(&opts->gen, inst->address, opts->gen.clock_divider == 4 ? "Sub M68k: %X @ %d\n" : "Main M68K: %X @ %d\n"); if ( - (inst->src.addr_mode > MODE_AREG && inst->src.addr_mode < MODE_IMMEDIATE) + (inst->src.addr_mode > MODE_AREG && inst->src.addr_mode < MODE_IMMEDIATE) || (inst->dst.addr_mode > MODE_AREG && inst->dst.addr_mode < MODE_IMMEDIATE) || (inst->op == M68K_BCC && (inst->src.params.immed & 1)) ) { @@ -1196,7 +1196,7 @@ context->aregs[7] = reset_vec[0] << 16 | reset_vec[1]; uint32_t address = reset_vec[2] << 16 | reset_vec[3]; //interrupt mask may have changed so force a sync - sync_components(context, address); + context->options->sync_components(context, address); start_68k_context(context, address); } diff -r 3748a2a8a4b7 -r cc13c100b027 m68k_core.h --- a/m68k_core.h Sat Jan 01 18:54:46 2022 -0800 +++ b/m68k_core.h Sun Jan 30 22:29:29 2022 -0800 @@ -25,6 +25,8 @@ #define M68K_STATUS_TRACE 0x80 typedef void (*start_fun)(uint8_t * addr, void * context); +typedef struct m68k_context m68k_context; +typedef m68k_context *(*sync_fun)(m68k_context * context, uint32_t address); typedef struct { code_ptr impl; @@ -59,6 +61,7 @@ code_ptr set_sr; code_ptr set_ccr; code_ptr bp_stub; + sync_fun sync_components; code_info extra_code; movem_fun *big_movem; uint32_t num_movem; @@ -66,7 +69,6 @@ code_word prologue_start; } m68k_options; -typedef struct m68k_context m68k_context; typedef void (*m68k_debug_handler)(m68k_context *context, uint32_t pc); typedef struct { @@ -106,7 +108,7 @@ void translate_m68k_stream(uint32_t address, m68k_context * context); void start_68k_context(m68k_context * context, uint32_t address); void resume_68k(m68k_context *context); -void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider); +void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun sync_components); m68k_context * init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler); void m68k_reset(m68k_context * context); void m68k_options_free(m68k_options *opts); diff -r 3748a2a8a4b7 -r cc13c100b027 m68k_core_x86.c --- a/m68k_core_x86.c Sat Jan 01 18:54:46 2022 -0800 +++ b/m68k_core_x86.c Sun Jan 30 22:29:29 2022 -0800 @@ -422,8 +422,8 @@ } dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (op->params.regs.pri == 7 ? 2 :1)); if (!dst || ( - inst->op != M68K_MOVE && inst->op != M68K_MOVEM - && inst->op != M68K_SUBX && inst->op != M68K_ADDX + inst->op != M68K_MOVE && inst->op != M68K_MOVEM + && inst->op != M68K_SUBX && inst->op != M68K_ADDX && inst->op != M68K_ABCD && inst->op != M68K_SBCD )) { cycles(&opts->gen, PREDEC_PENALTY); @@ -817,7 +817,7 @@ void translate_m68k_bcc(m68k_options * opts, m68kinst * inst) { code_info *code = &opts->gen.code; - + int32_t disp = inst->src.params.immed; uint32_t after = inst->address + 2; if (inst->extra.cond == COND_TRUE) { @@ -827,11 +827,11 @@ uint8_t cond = m68k_eval_cond(opts, inst->extra.cond); code_ptr do_branch = code->cur + 1; jcc(code, cond, do_branch); - + cycles(&opts->gen, inst->variant == VAR_BYTE ? 8 : 12); code_ptr done = code->cur + 1; jmp(code, done); - + *do_branch = code->cur - (do_branch + 1); cycles(&opts->gen, 10); code_ptr dest_addr = get_native_address(opts, after + disp); @@ -841,7 +841,7 @@ dest_addr = code->cur + 256; } jmp(code, dest_addr); - + *done = code->cur - (done + 1); } } @@ -1310,7 +1310,7 @@ { code_info *code = &opts->gen.code; uint8_t size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size; - + uint32_t numcycles; if ((inst->op == M68K_ADDX || inst->op == M68K_SUBX) && inst->src.addr_mode != MODE_REG) { numcycles = 4; @@ -1322,7 +1322,7 @@ } else if (inst->dst.addr_mode == MODE_REG) { numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE ? 8 : 6; } else if (inst->dst.addr_mode == MODE_AREG) { - numcycles = numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE + numcycles = numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE || inst->extra.size == OPSIZE_WORD ? 8 : 6; } else { numcycles = 4; @@ -1331,11 +1331,11 @@ numcycles = 4; } cycles(&opts->gen, numcycles); - + if (inst->op == M68K_ADDX || inst->op == M68K_SUBX) { flag_to_carry(opts, FLAG_X); } - + if (src_op->mode == MODE_REG_DIRECT) { if (dst_op->mode == MODE_REG_DIRECT) { op_rr(code, inst, src_op->base, dst_op->base, size); @@ -1500,7 +1500,7 @@ //destination is in memory so we need to preserve scratch2 for the write at the end push_r(code, opts->gen.scratch2); } - + //reg to reg takes 6 cycles, mem to mem is 4 cycles + all the operand fetch/writing (including 2 cycle predec penalty for first operand) cycles(&opts->gen, inst->dst.addr_mode != MODE_REG ? BUS : BUS + 2); uint8_t other_reg; @@ -1762,7 +1762,7 @@ force = dividend >> 31; quotient = quotient << 1 | bit; dividend = dividend << 1; - + if (force || dividend >= divisor_shift) { dividend -= divisor_shift; cycles += force ? 4 : 6; @@ -1784,7 +1784,7 @@ if (divisor_shift & 0x80000000) { divisor_shift = 0 - divisor_shift; } - + uint32_t cycles = 12; if (dividend & 0x80000000) { //dvs10 @@ -1805,7 +1805,7 @@ { quotient = quotient << 1 | bit; dividend = dividend << 1; - + if (dividend >= divisor_shift) { dividend -= divisor_shift; cycles += 6; @@ -1824,7 +1824,7 @@ quotient = quotient << 1; } cycles += 4; - + context->flags[FLAG_V] = 0; if (orig_divisor & 0x80000000) { cycles += 16; //was 10 @@ -1894,7 +1894,7 @@ cmp_ir(code, 0, opts->gen.scratch1, SZ_D); code_ptr not_zero = code->cur+1; jcc(code, CC_NZ, not_zero); - + //TODO: Check that opts->trap includes the cycles conumed by the first trap0 microinstruction cycles(&opts->gen, 4); uint32_t isize = 2; @@ -1917,7 +1917,7 @@ mov_ir(code, VECTOR_INT_DIV_ZERO, opts->gen.scratch2, SZ_D); mov_ir(code, inst->address+isize, opts->gen.scratch1, SZ_D); jmp(code, opts->trap); - + *not_zero = code->cur - (not_zero + 1); code_ptr end = NULL; if (inst->op == M68K_DIVU) { @@ -1926,13 +1926,13 @@ cmp_rr(code, opts->gen.scratch1, opts->gen.scratch2, SZ_D); code_ptr not_overflow = code->cur+1; jcc(code, CC_C, not_overflow); - + //overflow seems to always set the N and clear Z update_flags(opts, N1|Z0|V1); cycles(&opts->gen, 10); end = code->cur+1; jmp(code, end); - + *not_overflow = code->cur - (not_overflow + 1); } call(code, opts->gen.save_context); @@ -1941,14 +1941,14 @@ call_args(code, (code_ptr)(inst->op == M68K_DIVU ? divu : divs), 3, opts->gen.scratch2, opts->gen.context_reg, opts->gen.scratch1); pop_r(code, opts->gen.context_reg); mov_rr(code, RAX, opts->gen.scratch1, SZ_D); - + call(code, opts->gen.load_context); - + if (inst->op == M68K_DIVU) { cmp_ir(code, 0, opts->gen.scratch1, SZ_W); update_flags(opts, V0|Z|N); } - + if (dst_op->mode == MODE_REG_DIRECT) { mov_rr(code, opts->gen.scratch1, dst_op->base, SZ_D); } else { @@ -2048,7 +2048,7 @@ call(code, opts->gen.load_context); pop_r(code, opts->gen.scratch1); } - + uint8_t dst_reg; if (dst_op->mode == MODE_REG_DIRECT) { dst_reg = dst_op->base; @@ -2217,11 +2217,11 @@ bt_irdisp(code, BIT_SUPERVISOR, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B); code_ptr in_sup_mode = code->cur + 1; jcc(code, CC_C, code->cur + 2); - + ldi_native(opts, VECTOR_PRIV_VIOLATION, opts->gen.scratch2); ldi_native(opts, inst->address, opts->gen.scratch1); jmp(code, opts->trap); - + *in_sup_mode = code->cur - (in_sup_mode + 1); } @@ -2544,11 +2544,11 @@ m68k_options * opts = context->options; code_info native; native.cur = native_addr ? native_addr : get_native_address(context->options, address); - + if (!native.cur) { return; } - + if (*native.cur != opts->prologue_start) { //instruction has already been patched, probably for retranslation return; @@ -2557,12 +2557,12 @@ native.stack_off = 0; code_ptr start_native = native.cur; mov_ir(&native, address, opts->gen.scratch1, SZ_D); - - + + call(&native, opts->bp_stub); } -void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider) +void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider, sync_fun sync_components) { memset(opts, 0, sizeof(*opts)); opts->gen.memmap = memmap; @@ -2612,6 +2612,7 @@ opts->gen.limit = RBP; opts->gen.scratch1 = RCX; opts->gen.align_error_mask = 1; + opts->sync_components = sync_components; opts->gen.native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS); @@ -2695,7 +2696,7 @@ push_r(code, opts->gen.scratch1); xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D); - call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); + call_args_abi(code, (code_ptr)opts->sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); pop_r(code, RSI); //restore saved address from opts->gen.scratch1 push_r(code, RAX); //save context pointer for later call_args(code, (code_ptr)get_native_address_trans, 2, RAX, RSI); @@ -2713,7 +2714,7 @@ push_r(code, opts->gen.scratch2); call(code, opts->gen.save_context); xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D); - call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); + call_args_abi(code, (code_ptr)opts->sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR); call(code, opts->gen.load_context); pop_r(code, opts->gen.scratch2); @@ -2722,14 +2723,14 @@ retn(code); opts->gen.handle_code_write = (code_ptr)m68k_handle_code_write; - + check_alloc_code(code, 256); opts->gen.handle_align_error_write = code->cur; code->cur += 256; check_alloc_code(code, 256); opts->gen.handle_align_error_read = code->cur; code->cur += 256; - + opts->read_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_16, NULL); opts->read_8 = gen_mem_fun(&opts->gen, memmap, num_chunks, READ_8, NULL); opts->write_16 = gen_mem_fun(&opts->gen, memmap, num_chunks, WRITE_16, NULL); @@ -2830,7 +2831,7 @@ } } retn(code); - + code_info tmp_code = *code; code->cur = opts->gen.handle_align_error_write; code->last = code->cur + 256; @@ -2893,7 +2894,7 @@ call(code, opts->native_addr_and_sync); cycles(&opts->gen, 18); jmp_r(code, opts->gen.scratch1); - + code->cur = opts->gen.handle_align_error_read; code->last = code->cur + 256; //unwind the stack one functinon call @@ -2955,7 +2956,7 @@ call(code, opts->native_addr_and_sync); cycles(&opts->gen, 18); jmp_r(code, opts->gen.scratch1); - + *code = tmp_code; opts->gen.handle_cycle_limit_int = code->cur; @@ -2974,16 +2975,16 @@ *no_trace = code->cur - (no_trace + 1); //handle interrupts cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D); - code_ptr do_int = code->cur + 2; + code_ptr do_int = code->cur + 2; jcc(code, CC_NC, do_int+512);//force 32-bit displacement //handle component synchronization cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, sync_cycle), opts->gen.cycles, SZ_D); skip_sync = code->cur + 1; jcc(code, CC_C, code->cur + 2); call(code, opts->gen.save_context); - call_args_abi(code, (code_ptr)sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); + call_args_abi(code, (code_ptr)opts->sync_components, 2, opts->gen.context_reg, opts->gen.scratch1); mov_rr(code, RAX, opts->gen.context_reg, SZ_PTR); - jmp(code, opts->gen.load_context); + call(code, opts->gen.load_context); *skip_sync = code->cur - (skip_sync+1); cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, should_return), SZ_B); code_ptr do_ret = code->cur + 1; @@ -3031,9 +3032,9 @@ pop_r(code, opts->gen.scratch2); add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); jmp_r(code, opts->gen.scratch1); - + code->stack_off = tmp_stack_off; - + *((uint32_t *)do_int) = code->cur - (do_int+4); //implement 1 instruction latency cmp_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); @@ -3048,10 +3049,10 @@ cmp_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); code_ptr already_int_num = code->cur + 1; jcc(code, CC_NZ, already_int_num); - + mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch2, SZ_B); mov_rrdisp(code, opts->gen.scratch2, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); - + *already_int_num = code->cur - (already_int_num + 1); //save PC as stored in scratch1 for later push_r(code, opts->gen.scratch1); @@ -3133,10 +3134,10 @@ add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); jmp_r(code, opts->gen.scratch1); code->stack_off = tmp_stack_off; - + opts->handle_int_latch = code->cur; cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D); - code_ptr do_latch = code->cur + 1; + code_ptr do_latch = code->cur + 1; jcc(code, CC_NC, do_latch); retn(code); *do_latch = code->cur - (do_latch + 1); @@ -3177,7 +3178,7 @@ call(code, opts->native_addr_and_sync); cycles(&opts->gen, 18); jmp_r(code, opts->gen.scratch1); - + opts->retrans_stub = code->cur; call(code, opts->gen.save_context); push_r(code, opts->gen.context_reg); @@ -3186,8 +3187,8 @@ mov_rr(code, RAX, opts->gen.scratch1, SZ_PTR); call(code, opts->gen.load_context); jmp_r(code, opts->gen.scratch1); - - + + check_code_prologue(code); opts->bp_stub = code->cur; @@ -3224,6 +3225,6 @@ add_ir(code, check_int_size - patch_size, opts->gen.scratch1, SZ_PTR); jmp_r(code, opts->gen.scratch1); code->stack_off = tmp_stack_off; - + retranslate_calc(&opts->gen); } diff -r 3748a2a8a4b7 -r cc13c100b027 m68k_internal.h --- a/m68k_internal.h Sat Jan 01 18:54:46 2022 -0800 +++ b/m68k_internal.h Sun Jan 30 22:29:29 2022 -0800 @@ -106,8 +106,6 @@ #define PREDEC_PENALTY 2 extern char disasm_buf[1024]; -m68k_context * sync_components(m68k_context * context, uint32_t address); - void m68k_invalid(); void bcd_add(); void bcd_sub(); diff -r 3748a2a8a4b7 -r cc13c100b027 notes.txt --- a/notes.txt Sat Jan 01 18:54:46 2022 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -cmp.w , Dn 4(1/0) + time -cmp.l , Dn 6(1/0) + time -cmp.w #num, Dn 4(1/0) + 4(1/0) -cmp.l #num, Dn 6(1/0) + 8(2/0) - -cmpi.w #num, Dn 8(2/0) -cmpi.l #num, Dn 14(3/0) - - -movem - -subtype field (bits 9-11) = 110 or 100 depending on direction -bit 8 = 0 -bit 7 = 1 -bit 6 = size - - - -x86-64 registers in 68K core - -1. native stack pointer -2. current cycle count -3. target cycle count -4. cartridge address -5. work ram address -6. scratch register -7. context pointer (contains 68K registers and memory pointers not in registers) -8. status register (maybe, depends on how well I can abuse native x86 status stuff) -Rest of registers used for holding 68K registers - -rax = cycle counter -bl = N flag -bh = V flag -rcx = scratch register -dl = Z flag -dh = C flag -rbp = target cycle count -rsi = context pointer -rdi = scratch register -r8 = cartridge address -r9 = work ram address -r10 = d0 -r11 = d1 -r12 = d2 -r13 = a0 -r14 = a1 -r15 = a7 -rsp = native stack pointer - -68K context: -uint8_t flags[5]; -uint8_t pad??[3] -uint32_t dregs[8]; //8 + 4 * reg -uint32_t aregs[8]; //40 + 4 * reg -..... - -x86-64 registers in Z80 core - -ax = HL -bx = BC -cx = DE -dx = IX -ebp = current cycle count -rsi = context pointer -edi = target cycle count -rsp = native stack pointer -r8 = IY -r9 = SP -r10 = A (maybe AF?) -r11 = z80 ram address -r12 = cartridge address if bank is pointed at ROM -r13 = scratch1 -r14 = scratch2 -r15 = ?maybe z80 bank register? - - diff -r 3748a2a8a4b7 -r cc13c100b027 notes/cdd_mcu_notes.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/notes/cdd_mcu_notes.txt Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,429 @@ +CD Block Clock: 16.9344 MHz +MCU Clock divider: 2.1168 MHz (cd clock / 8) + +State at Reset: + Status buffer is filled with zero + Mechanism is reset + Emulation state set to ES_CLOSING + +HSCK active low +IRQ active high +CDCK active high + +Main Loop: + wait for subcode data for next frame + seems to be delivered via a "bank" IRQ + if servo is not in PLAY state + set DOUT and EMP to inactive + set MUTE to active + elif Q subcode CRC is not valid + set MUTE to active + elif Q subcode CONTROL field indicates a DATA track + set DOUT and MUTE to active + set EMP to inactive + else + set DOUT and MUTE to inactive + set EMP based on emphasis bit in Q subcode CONTROL field + run drive emulation state machine + if we received a valid command last loop + populate status buffer based on request format (see table below) + if drive error status is valid + override status format (2nd nibble) to RF_NOTREADY + put error status in STATUS (1st nibble) of status buffer + set error status valid to false + else + put drive status in STATUS (1st nibble) of status buffer + Communication with Host via gate array: + goes to fail state if HSCK is active (i.e. low) + IRQ line is asserted + Waits ~2ms (4234 cycles) for HSCK to go active (i.e lo) + on timeout, resets outputs to host to inactive and quits comms this loop + deasserts irq + Sends 10 status nibbles + nibbles are inverted + HSCK is handshake from host + CDCK is handshake frm MCU + minimum 77 cycles per nibble + + HSCK 1 (inactive) -> 0 (active) + nibble output + CDCK 0 (inactive) ->1 (active) + HSCK 0->1 + CDCK 1->0 + sets status sent flag + Gets 10 command nibbles + HSCK 1->0 + nibble input + CDCK 0->1 + HSCK 0->1 + CDCK 1->0 + Entire status/command transfer has timeout of ~3ms (6351 cycles) + on timeout, resets outputs to host to inactive and quits comms this loop + sets command received flag + Checksum of command is checked + if bad, resets outputs to host to inactive and quits comms this loop + set command valid flag + resets outputs to host to inactive + Runs command from host + Generally comms failures cause it to just run a Nop command + checksum failures cause drive error status to be set to STATUS_SUMERROR + command is specified by first 2 nibbles of command packet + second nibble must be zero or command is invalid + first nibble must be < 0xE and != 0x5 or command is invalid + command is sent to PC + command implementation is run + + +format Absolute + if Q subcode CRC is bad OR Q subcode Control ADR mode is not 1 or Q subcode tracknum indicates leadin + set status format (2nd nibble) to RF_NOTREADY + else + store Absolute (RF_A == 0) in status format (2nd nibble) + copy ATIME in BCD format from Q subcode buffer to 3rd through 8th nibble (inclusive) + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware +format Relative + if Q subcode CRC is bad OR Q subcode Control ADR mode is not 1 or Q subcode tracknum indicates leadin + set status format (2nd nibble) to RF_NOTREADY + else + store Relative (RF_R == 1) in status format (2nd nibble) + copy relative track TIME in BCD format from Q subcode buffer to 3rd through 8th nibble (inclusive) + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware +format Track + if Q subcode CRC is bad OR Q subcode Control ADR mode is not 1 or Q subcode tracknum indicates leadin + set status format (2nd nibble) to RF_NOTREADY + else + store Track (RF_T == 2) in status format (2nd nibble) + copy track number to 3rd and 4th status buffer nibbles + copy Q subcode CONTROL field to 5th status buffer nibble + copy Q subcode ADR field to 6th status buffer nibble + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware +format TOCO + if disc is not validated + set status format (2nd nibble) to RF_NOTREADY + else + set status format (2nd nibble) to RF_TOCO(3) + copy leadout start in MM:SS:FF format (BCD) to 3rd through 8th nibble (inclusive) + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware +format TOCT + if disc is not validated + set status format (2nd nibble) to RF_NOTREADY + else + set status format (2nd nibble) to RF_TOCT(4) + copy first track number in BCD format to 3rd and 4th nibbles + copy last track number in BCD format to 5th and 6th nibbles + copy TOCTVERSION(0) in BCD format to 7th and 8th nibbles + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware +format TOCN + if disc is not validated + set status format (2nd nibble) to RF_NOTREADY + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware + else + set status format (2nd nibble) to RF_TOCN(5) + if requested trac is not valid + set status format (2nd nibble) to RF_NOTREADY(F) + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware + else + copy track start time in MM:SS:FF format (BCD) to 3rd through 8th nibble (includisve) + if track is a data track, force MSB of frame to 1 + notably, since frame numbers sould be <75 this bit would always be zero otherwise + store low nibble of BCD track number in 8th nibble +format Error + set status format (2nd nibble) to RF_E(6) + error number is placed in 3rd nibble + note emulator always writes 0 + store flags in 9th nibble + these seem to correspond to the data, emphasis and mute control bits sent to CD hardware + +state Stopping: + if servo is in STOP status + if disc is known present or disc status is unknown + set drive status to STOP + else + set drive status to NODISC +state TOCing: + if servo is in STOP status + if focus is in NOTFOCUSING status + set haveDisc to FALSE + set haveDiscValid to TRUE + set drive status to NODISC + else + do nothing this tick + elif haveDisc is FALSE + do nothing this tick (comments suggest this is an abnormal situation) + else + set drive status to TOCREAD + if servo is NOT in PLAY status + stop here + if haveTOCValid is FALSE + process current Q subcode data for TOC entry (see GetTOC) + if TOC reading is done + set haveTOCValid to true + SEEK command is set to mechanism for PBA of program area start + stop until next tick + else + stop until next tick + else + if havePAValid is TRUE (comments say this isn't supposed to happen) + PAUSE command is sent to mechanism + else + check Q subcode data to see if we have found the start of the Program Area + if we have + set havePAValid to TRUE + SEEK command for start of first track is sent to mechanism + PAUSE command is sent to mechanism + else if current block Q subcode is invalid or not Mode 1 + SEEK to next block + else + SEEK forward 250 blocks +state Reading: +state Seeking: +state Pausing: +state Playing: +state Cuing: + These 5 states share a common implementation + if servo status is STOP + do nothing + elif servo status is SEEK + if mechanism error status is NOERROR + set drive status to SEEK + else + STOP command sent to mechanism + emulation state set to STOPPING + else + if servo status is PAUSE + if drive status is NOT DISCEND + set drive status to PAUSE + else + if Q subcode CRC is bad OR Q Control ADR is not mode 1 OR Q subcode tracknum is not lead out + set drive status to PLAY + else + PAUSE command sent to mechanism + set drive status to DISCEND + +state FWDing: + if servo status is STOP + do nothing + elif servo status is SEEK + if mechanism error status is NOERROR + set drive status to SCAN + else + STOP command is sent to mechanism + emulation state is set to STOPPING + elif servo status is PAUSE + set drive status to DISCEND + else: + set drive status to SCAN + if Q subcode CRC is valid AND Q Control ADR is mode 1 + if Q subcode tracknum indicates leadout + PAUSE command is sent to mechanism + set drive status to DISCEND + else + if scanClock is 0 + get ATIME from current Q subcode block + turn it into an LBA, then PBA + Add 100 to it + SEEK command sent to mechanism + reset scanClock to SCANPLAYTIME(10) +state RVSing: + if servo status is STOP + do nothing + elif servo status is SEEK + if mechanism error status is NOERROR + set drive status to SCAN + else + STOP command is sent to mechanism + emulation state is set to STOPPING + elif servo status is PAUSE + do nothing + else + set drive status to SCAN + if Q subcode CRC is valid AND Q Control ADR is mode 1 + if Q subcode tracknum indcates we're in the leadin area + SEEK command is sent to mechanism for beginning of first track + PLAY command is sent to mechanism + Set emulation state to PLAYING + else + if scanClock is 0 + get ATIME from current Q subcode block + turn it into an LBA, then PBA + subtract 140 from it + SEEK command sent to mechanism + reset scanClock to SCANPLAYTIME(10) +state Skipping: + if servo status is STOP + do nothing + elif servo status is SEEK + if mechanism error status is NOERROR + set drive status to TRACKING + else + STOP command is sent to mechanism + emulation state is set to STOPPING + elif servo status is PAUSE + set drive status to PAUSE + else + PAUSE command sent to mechanism +state DoorClosing: + if door state is CLOSING + set drive status to TRAYMOVING + elif door state is CLOSED + set drive status to STOP + set request format to absolute + set emulation state to CLOSED +state DoorClosed: + do nothing +state DoorOpening: + if door state is OPENING + set drive status to TRAYMOVING + elif door state is OPEN + set drive status to DOOROPEN + set emulation state to OPEN +state DoorOpen: + if door status is NOT one of OPEN or OPENING + set emulation state to CLOSING + +command Nop +command Stop + set request format to absolute + if drive status is not DOOROPEN and status is notTRAYMOVING + send stop command to mechanism + set emulation state to ES_STOPPING +command Report Request + 4th nibble specifies request type + RF_A(0) + set absolute time format + RF_R(1) + set relative time format + RF_T(2) + request current track info + RF_TOCO(3) + request disc completion time + will be ignored if disc has not been validated yet + drive error status will be set to COMMANDERROR + request format will be set back to absolute + RF_TOCT(4) + request start/end tracks on disc + if disc is validated and drive is stopped + will seek to start of disc and pause there + emulation state set to TOCing + if disc is validated and drive is not stopped + only request format is changed + else + prepares memory state for TOC read + will seek to start of disc and tell mechanism to play + emulation state set to TOCing + RF_TOCN(5) + request start time of track N + if drivestatus is DOOROPEN, TRAYMOVING or STOP + command will be ignore dand drive error status will be set to COMMANDERROR + request format will be set back to absolute + requested track number is in the 5th and 6th nibbles in BCD format + SEEK command sent to mechanism for start of disc + PAUSE command sent to mechansim + emulation state set to TOCing + RF_E(6) + request error info + +command Read + if drive status is DOOROPEN or TRAYMOVING + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + location specified in MM:SS:FF format (BCD) in the 3rd through 8th nibbles (inclusive) + location is converted to an LBA + if location >= leadout start + set drive error status to COMMANDERROR and stop processing command + program area offset is added to location to convert it to a physical block address + PBA is adjusted back 4 + SEEK command is sent to mechanism for adjusted PBA + PLAY command is sent to mechanism + Emulation state set to Reading +command Seek + Same as Read, except sends Pause instead of Play for second command to mechanism + Emulation state set to Seeking +command Pause + if drive status is DOOROPEN or TRAYMOVING or SCAN + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + if drive status is STOP + calculates PBA of first track from TOC + PBA is NOT adjusted back + send SEEK command to mechanism for calculated PBA + send PAUSE to mechanism + set emulation state to Pausing +command Play + if drive status is DOOROPEN or TRAYMOVING or DISCEND or DISCIN + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + if drive status is TOCREAD or STOP + calculates PBA of first track from TOC + PBA is adjusted back by 4 + SEEK command is sent to mechanism for adjusted PBA + send PLAY command to mechanism + set emulation state to Playing +command Fwd + if drive status is NOT one of PLAY, PAUSE or DISCIN + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + Send PLAY command to mechanism + Emulation state set to Fwding + scanClock is set to SCANPLAYTIME(10)?? +command Rvs + if drive status is NOT one of PLAY, PAUSE or DISCEND + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + Send PLAY command to mechanism + Emulation state set to Rvsing +command TrackSkip + if drive status is NOT one of PLAY, PAUSE or DISCEND + set drive error status to COMMANDERROR and stop processing command + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + direction to skip is in 4th nibble of command + number of tracks to skip is a 16-bit value in the 5th-8th nibbles + note tese are physical tracks i.e. loops of the spiral + SKIP command is sent to mechanism + PAUSE command is sent to mechanism + emulation state is set to TSkping +command TrackCue + if drive status is NOT one of STOP, PLAY, PAUSE, DISCEND, DISCIN or TOCREAD + set drive error status to COMMANDERROR and stop processing command + if drive status is TOCREAD + check request format, if it's TOCT or TOCN, force it to absolute + if disc is not validated + set drive error status to COMMANDERROR and stop processing command + track number is in nibbles 3 and 4 in BCD format + calculates PBA of requested track from TOC + Sends SEEK command to mechanism for calculated PBA + if drive status at start of command was STOP, DISCEND or DISCIN + send PLAY command to mechanism + emulation state is set to CUING +command DoorClose + if drive status is NOT one of DOOROPEN or TRAYMOVING + set drive error status to COMMANDERROR and stop processing command + CLOSE command is set to mechanism + emulation state is set to CLOSING +command DoorOpen + if drive status is DOOROPEN + set drive error status to COMMANDERROR and stop processing command + if drive servo is not stopped: + set request format to absolute + OPEN command is sent to mechanism + emulation state set to OPENING + disc, TOC and program area validitly flags are cleared diff -r 3748a2a8a4b7 -r cc13c100b027 notes/notes.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/notes/notes.txt Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,76 @@ +cmp.w , Dn 4(1/0) + time +cmp.l , Dn 6(1/0) + time +cmp.w #num, Dn 4(1/0) + 4(1/0) +cmp.l #num, Dn 6(1/0) + 8(2/0) + +cmpi.w #num, Dn 8(2/0) +cmpi.l #num, Dn 14(3/0) + + +movem + +subtype field (bits 9-11) = 110 or 100 depending on direction +bit 8 = 0 +bit 7 = 1 +bit 6 = size + + + +x86-64 registers in 68K core + +1. native stack pointer +2. current cycle count +3. target cycle count +4. cartridge address +5. work ram address +6. scratch register +7. context pointer (contains 68K registers and memory pointers not in registers) +8. status register (maybe, depends on how well I can abuse native x86 status stuff) +Rest of registers used for holding 68K registers + +rax = cycle counter +bl = N flag +bh = V flag +rcx = scratch register +dl = Z flag +dh = C flag +rbp = target cycle count +rsi = context pointer +rdi = scratch register +r8 = cartridge address +r9 = work ram address +r10 = d0 +r11 = d1 +r12 = d2 +r13 = a0 +r14 = a1 +r15 = a7 +rsp = native stack pointer + +68K context: +uint8_t flags[5]; +uint8_t pad??[3] +uint32_t dregs[8]; //8 + 4 * reg +uint32_t aregs[8]; //40 + 4 * reg +..... + +x86-64 registers in Z80 core + +ax = HL +bx = BC +cx = DE +dx = IX +ebp = current cycle count +rsi = context pointer +edi = target cycle count +rsp = native stack pointer +r8 = IY +r9 = SP +r10 = A (maybe AF?) +r11 = z80 ram address +r12 = cartridge address if bank is pointed at ROM +r13 = scratch1 +r14 = scratch2 +r15 = ?maybe z80 bank register? + + diff -r 3748a2a8a4b7 -r cc13c100b027 notes/sf2_50mhz_analysis.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/notes/sf2_50mhz_analysis.txt Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,173 @@ +HSYNC End +Plane A: D13C +??slot?? +tile +tile +Plane B: F29C +?sprite tile slot? +tile +tile +Plane A: D100 +68K Slot +tile +tile +Plane B: F2A0 +?sprite table slot? +tile +tile +Plane A: D104 +68K slot +tile +tile +Plane B: F2A4 +?sprite table slot? +tile +tile +Plane A: D108 +68K slot +?tile? +?tile? +Plane B: F2A8 +?sprite table slot? +tile +tile +Plane A: D10C +refresh +?tile? +?tile? +Plane B: F22C +?sprite table slot? +tile +tile +Plane A: D110 +68K slot +?tile? +?tile? +Plane B: F230 +?sprite table slot? +tile +tile +Plane A: D114 +68K slot +?tile? +?tile? +Plane B: F2F4 +?sprite table slot? +tile +tile +Plane A: D118 +68K slot +?tile? +?tile? +Plane B: F238 +?sprite table slot? +ttile tile +Plane A: D11C +refresh +?tile? +?tile? +Plane B: F23C +?sprite table slot? +tile +tile +Plane A: D120 +68K slot +?tile? +?tile? +Plane B: F240 +?sprite table slot? +tile +tile +Plane A: D124 +68K Slot +?tile? +?tile? +Plane B: F244 +?sprite table slot? +tile +tile +Plane A: D128 +68K Slot +?tile? +?tile? +Plane B: F3C8 +?sprite table slot? +tile +tile +Plane A: D12C +refresh +?tile? +?tile? +Plane B: F24C +?sprite table slot? +tile +tile +Plane A: D130 +68K slot +?tile? +?tile? +Plane B: F250 +?sprite table slot? +tile +tile +Plane A: D134 +68K Slot +?tile? +?tile? +Plane B: F254 +?sprite table slot? +tile +tile +Plane A: D138 +68K slot +?tile? +?tile? +Plane B: F3D8 +?sprite table slot? +tile +tile +Plane A: D13C +refresh +tile +tile +Plane B: F3DC +?sprite table slot? +tile +tile +--------------- +68K Slot +68K Slot +sprite tile slot +sprite tile slot +sprite tile slot +sprite tile slot +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +68K Slot +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +HSYNC Start +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?unkown tile slot? +68K Slot +Horizontal Scroll +?sprite tile slot? +?sprite tile slot? +?sprite tile slot? +?unkown tile slot? diff -r 3748a2a8a4b7 -r cc13c100b027 notes/sf2_vram_map.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/notes/sf2_vram_map.txt Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,64 @@ +0000: single color blocks +0200: Random symbols???? +0400-0C00: Font +0C00-0E??: Score font +0E??: Player name displays + +2000-3000: floor (mostly, some garbage around 2800 and 2A00) +3000-6C00: background +6C00-8000: not graphics +8000-~8A00: bat/bird sprites, special attack text +8A00-9000: ????? +9000-9200: More special attack text +9200-9600: another font +9800-AA00: special attack effects? +AC00-B000: ???? +B000-B500: character sprite +???? +C000-C500: chracter sprite +D000-DA00: window name table +DA00-DC00: Sprite attribute table +DC00-E000: horizontal scroll data +E000-????: plane A&B name table +C500-FFFF: not graphics + +VDP Registers: +Mode +00 - 14 +01 - 64 +Scroll A Name Table Address: E000 +02 - 38 +Window Name Table Address: D000 +03 - 34 +Scroll B Name Table Address: E000 +04 - 07 +Sprite Attribute Table Address: DA00 +05 - 6D +06 - 00 +Backdrop color - 0 +07 - 00 +08 - 00 +09 - 00 +HINT Counter +0A - AF +Mode - Full screen vertical scroll, line horizontal scroll, external ints disabled, 32 cell display, no interlacing +0B - 03 +0C - 00 +H Scroll Data Address: DC00 +0D - 37 +0E - 00 +Auto increment +0F - 02 +Scroll Size +10 - 11 +Window H Pos +11 - 00 +Window V Pos +12 - 05 +DMA transfer length +13 - 00 +14 - 00 +DMA source address and mode +15 - DB +16 - CF +17 - 7F diff -r 3748a2a8a4b7 -r cc13c100b027 romdb.c --- a/romdb.c Sat Jan 01 18:54:46 2022 -0800 +++ b/romdb.c Sun Jan 30 22:29:29 2022 -0800 @@ -38,11 +38,14 @@ return "SRAM"; } -tern_node *load_rom_db() +tern_node *get_rom_db() { - tern_node *db = parse_bundled_config("rom.db"); + static tern_node *db; if (!db) { - fatal_error("Failed to load ROM DB\n"); + db = parse_bundled_config("rom.db"); + if (!db) { + fatal_error("Failed to load ROM DB\n"); + } } return db; } @@ -472,6 +475,19 @@ info.rom_size = rom_size; add_memmap_header(&info, rom, rom_size, base_map, base_chunks); info.port1_override = info.port2_override = info.ext_override = info.mouse_mode = NULL; + info.wants_cd = 0; + for (uint32_t offset = 0x190; offset < rom_size && offset < 0x1A0; offset++) + { + if (rom[offset] == 'F') { + // probably a codemasters game with a garbage header + break; + } + if (rom[offset] == 'C') { + info.wants_cd = 1; + break; + } + } + return info; } @@ -1052,6 +1068,7 @@ info.port1_override = info.port2_override = info.ext_override = NULL; } info.mouse_mode = tern_find_ptr(entry, "mouse_mode"); + info.wants_cd = !strcmp(tern_find_ptr_default(entry, "wants_cd", "no"), "yes"); return info; } diff -r 3748a2a8a4b7 -r cc13c100b027 romdb.h --- a/romdb.h Sat Jan 01 18:54:46 2022 -0800 +++ b/romdb.h Sun Jan 30 22:29:29 2022 -0800 @@ -79,12 +79,13 @@ uint8_t mapper_type; uint8_t regions; uint8_t is_save_lock_on; //Does the save buffer actually belong to a lock-on cart? + uint8_t wants_cd; }; #define GAME_ID_OFF 0x183 #define GAME_ID_LEN 8 -tern_node *load_rom_db(); +tern_node *get_rom_db(); rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, memmap_chunk const *base_map, uint32_t base_chunks); rom_info configure_rom_heuristics(uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks); uint8_t translate_region_char(uint8_t c); diff -r 3748a2a8a4b7 -r cc13c100b027 segacd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/segacd.c Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,1208 @@ +#include +#include +#include "cd_graphics.h" +#include "genesis.h" +#include "util.h" + +#define SCD_MCLKS 50000000 +#define SCD_PERIPH_RESET_CLKS (SCD_MCLKS / 10) +#define TIMER_TICK_CLKS 1536 + +enum { + GA_SUB_CPU_CTRL, + GA_MEM_MODE, + GA_CDC_CTRL, + GA_CDC_REG_DATA, + GA_CDC_HOST_DATA, + GA_CDC_DMA_ADDR, + GA_STOP_WATCH, + GA_COMM_FLAG, + GA_COMM_CMD0, + GA_COMM_CMD1, + GA_COMM_CMD2, + GA_COMM_CMD3, + GA_COMM_CMD4, + GA_COMM_CMD5, + GA_COMM_CMD6, + GA_COMM_CMD7, + GA_COMM_STATUS0, + GA_COMM_STATUS1, + GA_COMM_STATUS2, + GA_COMM_STATUS3, + GA_COMM_STATUS4, + GA_COMM_STATUS5, + GA_COMM_STATUS6, + GA_COMM_STATUS7, + GA_TIMER, + GA_INT_MASK, + GA_CDD_FADER, + GA_CDD_CTRL, + GA_CDD_STATUS0, + GA_CDD_STATUS1, + GA_CDD_STATUS2, + GA_CDD_STATUS3, + GA_CDD_STATUS4, + GA_CDD_CMD0, + GA_CDD_CMD1, + GA_CDD_CMD2, + GA_CDD_CMD3, + GA_CDD_CMD4, + GA_FONT_COLOR, + GA_FONT_BITS, + GA_FONT_DATA0, + GA_FONT_DATA1, + GA_FONT_DATA2, + GA_FONT_DATA3, + + GA_HINT_VECTOR = GA_CDC_REG_DATA +}; +//GA_SUB_CPU_CTRL +#define BIT_IEN2 0x8000 +#define BIT_IFL2 0x0100 +#define BIT_LEDG 0x0200 +#define BIT_LEDR 0x0100 +#define BIT_SBRQ 0x0002 +#define BIT_SRES 0x0001 +#define BIT_PRES 0x0001 +//GA_MEM_MODE +#define MASK_PROG_BANK 0x00C0 +#define BIT_OVERWRITE 0x0010 +#define BIT_UNDERWRITE 0x0008 +#define MASK_PRIORITY (BIT_OVERWRITE|BIT_UNDERWRITE) +#define BIT_MEM_MODE 0x0004 +#define BIT_DMNA 0x0002 +#define BIT_RET 0x0001 + +//GA_CDC_CTRL +#define BIT_EDT 0x8000 +#define BIT_DSR 0x4000 + +enum { + DST_MAIN_CPU = 2, + DST_SUB_CPU, + DST_PCM_RAM, + DST_PROG_RAM, + DST_WORD_RAM = 7 +}; + +//GA_INT_MASK +#define BIT_MASK_IEN1 0x0002 +#define BIT_MASK_IEN2 0x0004 +#define BIT_MASK_IEN3 0x0008 +#define BIT_MASK_IEN4 0x0010 +#define BIT_MASK_IEN5 0x0020 +#define BIT_MASK_IEN6 0x0040 + +//GA_CDD_CTRL +#define BIT_HOCK 0x0004 + +static void *prog_ram_wp_write16(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + //if (!(cd->gate_array[GA_MEM_MODE] & (1 << ((address >> 9) + 8)))) { + if (address >= ((cd->gate_array[GA_MEM_MODE] & 0xFF00) << 1)) { + cd->prog_ram[address >> 1] = value; + m68k_invalidate_code_range(m68k, address, address + 2); + } + return vcontext; +} + +static void *prog_ram_wp_write8(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + if (address >= ((cd->gate_array[GA_MEM_MODE] & 0xFF00) << 1)) { + ((uint8_t *)cd->prog_ram)[address ^ 1] = value; + m68k_invalidate_code_range(m68k, address, address + 1); + } + return vcontext; +} + +static uint16_t word_ram_2M_read16(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + //TODO: fixme for interleaving + uint16_t* bank = m68k->mem_pointers[1]; + uint16_t raw = bank[address >> 2]; + if (address & 2) { + return (raw & 0xF) | (raw << 4 & 0xF00); + } else { + return (raw >> 4 & 0xF00) | (raw >> 8 & 0xF); + } +} + +static uint8_t word_ram_2M_read8(uint32_t address, void *vcontext) +{ + uint16_t word = word_ram_2M_read16(address, vcontext); + if (address & 1) { + return word; + } + return word >> 8; +} + +static void *word_ram_2M_write8(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + value &= 0xF; + uint16_t priority = cd->gate_array[GA_MEM_MODE] & MASK_PRIORITY; + + if (priority == BIT_OVERWRITE && !value) { + return vcontext; + } + if (priority == BIT_UNDERWRITE) { + if (!value) { + return vcontext; + } + uint8_t old = word_ram_2M_read8(address, vcontext); + if (old) { + return vcontext; + } + } + uint16_t* bank = m68k->mem_pointers[1]; + uint16_t raw = bank[address >> 2]; + uint16_t shift = ((address & 3) * 4); + raw &= ~(0xF000 >> shift); + raw |= value << (12 - shift); + bank[address >> 2] = raw; + return vcontext; +} + + +static void *word_ram_2M_write16(uint32_t address, void *vcontext, uint16_t value) +{ + word_ram_2M_write8(address, vcontext, value >> 8); + return word_ram_2M_write8(address + 1, vcontext, value); +} + +static uint16_t word_ram_1M_read16(uint32_t address, void *vcontext) +{ + return 0; +} + +static uint8_t word_ram_1M_read8(uint32_t address, void *vcontext) +{ + return 0; +} + +static void *word_ram_1M_write16(uint32_t address, void *vcontext, uint16_t value) +{ + return vcontext; +} + +static void *word_ram_1M_write8(uint32_t address, void *vcontext, uint8_t value) +{ + return vcontext; +} + + +static uint16_t unmapped_prog_read16(uint32_t address, void *vcontext) +{ + return 0xFFFF; +} + +static uint8_t unmapped_prog_read8(uint32_t address, void *vcontext) +{ + return 0xFF; +} + +static void *unmapped_prog_write16(uint32_t address, void *vcontext, uint16_t value) +{ + return vcontext; +} + +static void *unmapped_prog_write8(uint32_t address, void *vcontext, uint8_t value) +{ + return vcontext; +} + +static uint16_t unmapped_word_read16(uint32_t address, void *vcontext) +{ + return 0xFFFF; +} + +static uint8_t unmapped_word_read8(uint32_t address, void *vcontext) +{ + return 0xFF; +} + +static void *unmapped_word_write16(uint32_t address, void *vcontext, uint16_t value) +{ + return vcontext; +} + +static void *unmapped_word_write8(uint32_t address, void *vcontext, uint8_t value) +{ + return vcontext; +} + +static uint16_t cell_image_read16(uint32_t address, void *vcontext) +{ + return 0xFFFF; +} + +static uint8_t cell_image_read8(uint32_t address, void *vcontext) +{ + return 0xFF; +} + +static void *cell_image_write16(uint32_t address, void *vcontext, uint16_t value) +{ + return vcontext; +} + +static void *cell_image_write8(uint32_t address, void *vcontext, uint8_t value) +{ + return vcontext; +} + +static uint8_t pcm_read8(uint32_t address, void *vcontext) +{ + return 0; +} + +static uint16_t pcm_read16(uint32_t address, void *vcontext) +{ + return 0xFF00 | pcm_read8(address+1, vcontext); +} + +static void *pcm_write8(uint32_t address, void *vcontext, uint8_t value) +{ + return vcontext; +} + +static void *pcm_write16(uint32_t address, void *vcontext, uint16_t value) +{ + return pcm_write8(address+1, vcontext, value); +} + + +static void timers_run(segacd_context *cd, uint32_t cycle) +{ + if (cycle <= cd->stopwatch_cycle) { + return; + } + uint32_t ticks = (cycle - cd->stopwatch_cycle) / TIMER_TICK_CLKS; + cd->stopwatch_cycle += ticks * TIMER_TICK_CLKS; + cd->gate_array[GA_STOP_WATCH] += ticks; + cd->gate_array[GA_STOP_WATCH] &= 0xFFF; + if (ticks && !cd->timer_value) { + --ticks; + cd->timer_value = cd->gate_array[GA_TIMER]; + } + if (ticks && cd->timer_value) { + while (ticks >= (cd->timer_value + 1)) { + ticks -= cd->timer_value + 1; + cd->timer_value = cd->gate_array[GA_TIMER]; + cd->timer_pending = 1; + } + cd->timer_value -= ticks; + if (!cd->timer_value) { + cd->timer_pending = 1; + } + } +} + +static void cdd_run(segacd_context *cd, uint32_t cycle) +{ + cdd_mcu_run(&cd->cdd, cycle, cd->gate_array + GA_CDD_CTRL, &cd->cdc); + lc8951_run(&cd->cdc, cycle); +} + +static uint32_t next_timer_int(segacd_context *cd) +{ + if (cd->timer_pending) { + return cd->stopwatch_cycle; + } + if (cd->timer_value) { + return cd->stopwatch_cycle + TIMER_TICK_CLKS * cd->timer_value; + } + if (cd->gate_array[GA_TIMER]) { + return cd->stopwatch_cycle + TIMER_TICK_CLKS * (cd->gate_array[GA_TIMER] + 1); + } + return CYCLE_NEVER; +} + +static void calculate_target_cycle(m68k_context * context) +{ + segacd_context *cd = context->system; + context->int_cycle = CYCLE_NEVER; + uint8_t mask = context->status & 0x7; + if (mask < 5) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN5) { + uint32_t cdc_cycle = lc8951_next_interrupt(&cd->cdc); + //CDC interrupts only generated on falling edge of !INT signal + if (cd->cdc_int_ack) { + if (cdc_cycle > cd->cdc.cycle) { + cd->cdc_int_ack = 0; + } else { + cdc_cycle = CYCLE_NEVER; + } + } + if (cdc_cycle < context->int_cycle) { + context->int_cycle = cdc_cycle; + context->int_num = 5; + } + } + if (mask < 4) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN4) { + uint32_t cdd_cycle = cd->cdd.int_pending ? context->current_cycle : cd->cdd.next_int_cycle; + if (cdd_cycle < context->int_cycle) { + context->int_cycle = cdd_cycle; + context->int_num = 4; + } + } + if (mask < 3) { + uint32_t next_timer; + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN3) { + uint32_t next_timer_cycle = next_timer_int(cd); + if (next_timer_cycle < context->int_cycle) { + context->int_cycle = next_timer_cycle; + context->int_num = 3; + } + } + if (mask < 2) { + if (cd->int2_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { + context->int_cycle = cd->int2_cycle; + context->int_num = 2; + } + if (mask < 1) { + if (cd->graphics_int_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN1)) { + context->int_cycle = cd->graphics_int_cycle; + context->int_num = 1; + } + } + } + } + } + } + if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { + context->int_pending = INT_PENDING_NONE; + } + if (context->current_cycle >= context->sync_cycle) { + context->should_return = 1; + context->target_cycle = context->current_cycle; + return; + } + if (context->status & M68K_STATUS_TRACE || context->trace_pending) { + context->target_cycle = context->current_cycle; + return; + } + context->target_cycle = context->sync_cycle < context->int_cycle ? context->sync_cycle : context->int_cycle; +} + +static uint16_t sub_gate_read16(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + uint32_t reg = address >> 1; + switch (reg) + { + case GA_SUB_CPU_CTRL: { + uint16_t value = cd->gate_array[reg] & 0xFFFE; + if (cd->periph_reset_cycle == CYCLE_NEVER || (m68k->current_cycle - cd->periph_reset_cycle) > SCD_PERIPH_RESET_CLKS) { + value |= BIT_PRES; + } + return value; + } + case GA_MEM_MODE: + return cd->gate_array[reg] & 0xFF1F; + case GA_CDC_CTRL: + cdd_run(cd, m68k->current_cycle); + return cd->gate_array[reg] | cd->cdc.ar; + case GA_CDC_REG_DATA: + cdd_run(cd, m68k->current_cycle); + return lc8951_reg_read(&cd->cdc); + case GA_CDC_HOST_DATA: { + cdd_run(cd, m68k->current_cycle); + uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + if (dst == DST_SUB_CPU) { + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; + lc8951_resume_transfer(&cd->cdc, cd->cdc.cycle); + } + calculate_target_cycle(cd->m68k); + + } + return cd->gate_array[reg]; + } + case GA_STOP_WATCH: + case GA_TIMER: + timers_run(cd, m68k->current_cycle); + return cd->gate_array[reg]; + case GA_CDD_STATUS0: + case GA_CDD_STATUS1: + case GA_CDD_STATUS2: + case GA_CDD_STATUS3: + case GA_CDD_STATUS4: + cdd_run(cd, m68k->current_cycle); + return cd->gate_array[reg]; + break; + case GA_FONT_DATA0: + case GA_FONT_DATA1: + case GA_FONT_DATA2: + case GA_FONT_DATA3: { + uint16_t shift = 4 * (3 - (reg - GA_FONT_DATA0)); + uint16_t value = 0; + uint16_t fg = cd->gate_array[GA_FONT_COLOR] >> 4; + uint16_t bg = cd->gate_array[GA_FONT_COLOR] & 0xF; + for (int i = 0; i < 4; i++) { + uint16_t pixel = 0; + if (cd->gate_array[GA_FONT_BITS] & 1 << (shift + i)) { + pixel = fg; + } else { + pixel = bg; + } + value |= pixel << (i * 4); + } + return value; + case GA_STAMP_SIZE: + case GA_IMAGE_BUFFER_LINES: + //these two have bits that change based on graphics operations + cd_graphics_run(cd, m68k->current_cycle); + return cd->gate_array[reg]; + case GA_TRACE_VECTOR_BASE: + //write only + return 0xFFFF; + } + default: + return cd->gate_array[reg]; + } +} + +static uint8_t sub_gate_read8(uint32_t address, void *vcontext) +{ + uint16_t val = sub_gate_read16(address, vcontext); + return address & 1 ? val : val >> 8; +} + +static void *sub_gate_write16(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + uint32_t reg = address >> 1; + switch (reg) + { + case GA_SUB_CPU_CTRL: + cd->gate_array[reg] &= 0xF0; + cd->gate_array[reg] |= value & (BIT_LEDG|BIT_LEDR); + if (value & BIT_PRES) { + cd->periph_reset_cycle = m68k->current_cycle; + } + break; + case GA_MEM_MODE: { + uint16_t changed = value ^ cd->gate_array[reg]; + genesis_context *gen = cd->genesis; + if (changed & BIT_MEM_MODE) { + //FIXME: ram banks are supposed to be interleaved when in 2M mode + cd->gate_array[reg] &= ~BIT_DMNA; + if (value & BIT_MEM_MODE) { + //switch to 1M mode + gen->m68k->mem_pointers[cd->memptr_start_index + 1] = (value & BIT_RET) ? cd->word_ram + 0x10000 : cd->word_ram; + gen->m68k->mem_pointers[cd->memptr_start_index + 2] = NULL; + m68k->mem_pointers[0] = NULL; + m68k->mem_pointers[1] = (value & BIT_RET) ? cd->word_ram : cd->word_ram + 0x10000; + } else { + //switch to 2M mode + if (value & BIT_RET) { + //Main CPU will have word ram + gen->m68k->mem_pointers[cd->memptr_start_index + 1] = cd->word_ram; + gen->m68k->mem_pointers[cd->memptr_start_index + 2] = cd->word_ram + 0x10000; + m68k->mem_pointers[0] = NULL; + m68k->mem_pointers[1] = NULL; + } else { + //sub cpu will have word ram + gen->m68k->mem_pointers[cd->memptr_start_index + 1] = NULL; + gen->m68k->mem_pointers[cd->memptr_start_index + 2] = NULL; + m68k->mem_pointers[0] = cd->word_ram; + m68k->mem_pointers[1] = NULL; + } + } + m68k_invalidate_code_range(gen->m68k, cd->base + 0x200000, cd->base + 0x240000); + m68k_invalidate_code_range(m68k, 0x080000, 0x0E0000); + } else if (changed & BIT_RET) { + if (value & BIT_MEM_MODE) { + cd->gate_array[reg] &= ~BIT_DMNA; + //swapping banks in 1M mode + gen->m68k->mem_pointers[cd->memptr_start_index + 1] = (value & BIT_RET) ? cd->word_ram + 0x10000 : cd->word_ram; + m68k->mem_pointers[1] = (value & BIT_RET) ? cd->word_ram : cd->word_ram + 0x10000; + m68k_invalidate_code_range(gen->m68k, cd->base + 0x200000, cd->base + 0x240000); + m68k_invalidate_code_range(m68k, 0x080000, 0x0E0000); + } else if (value & BIT_RET) { + cd->gate_array[reg] &= ~BIT_DMNA; + //giving word ram to main CPU in 2M mode + gen->m68k->mem_pointers[cd->memptr_start_index + 1] = cd->word_ram; + gen->m68k->mem_pointers[cd->memptr_start_index + 2] = cd->word_ram + 0x10000; + m68k->mem_pointers[0] = NULL; + m68k_invalidate_code_range(gen->m68k, cd->base + 0x200000, cd->base + 0x240000); + m68k_invalidate_code_range(m68k, 0x080000, 0x0E0000); + } else { + value |= BIT_RET; + } + } + cd->gate_array[reg] &= 0xFFC2; + cd->gate_array[reg] |= value & (BIT_RET|BIT_MEM_MODE|MASK_PRIORITY); + break; + } + case GA_CDC_CTRL: + cdd_run(cd, m68k->current_cycle); + lc8951_ar_write(&cd->cdc, value); + //cd->gate_array[reg] &= 0xC000; + //apparently this clears EDT, should it also clear DSR? + cd->gate_array[reg] = value & 0x0700; + cd->cdc_dst_low = 0; + break; + case GA_CDC_REG_DATA: + cdd_run(cd, m68k->current_cycle); + printf("CDC write %X: %X @ %u\n", cd->cdc.ar, value, m68k->current_cycle); + lc8951_reg_write(&cd->cdc, value); + calculate_target_cycle(m68k); + break; + case GA_CDC_DMA_ADDR: + cdd_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value; + cd->cdc_dst_low = 0; + break; + case GA_STOP_WATCH: + //docs say you should only write zero to reset + //mcd-verificator comments suggest any value will reset + timers_run(cd, m68k->current_cycle); + cd->gate_array[reg] = 0; + break; + case GA_COMM_FLAG: + cd->gate_array[reg] &= 0xFF00; + cd->gate_array[reg] |= value & 0xFF; + break; + case GA_COMM_STATUS0: + case GA_COMM_STATUS1: + case GA_COMM_STATUS2: + case GA_COMM_STATUS3: + case GA_COMM_STATUS4: + case GA_COMM_STATUS5: + case GA_COMM_STATUS6: + case GA_COMM_STATUS7: + //no effects for these other than saving the value + cd->gate_array[reg] = value; + break; + case GA_TIMER: + timers_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFF; + calculate_target_cycle(m68k); + break; + case GA_INT_MASK: + cd->gate_array[reg] = value & (BIT_MASK_IEN6|BIT_MASK_IEN5|BIT_MASK_IEN4|BIT_MASK_IEN3|BIT_MASK_IEN2|BIT_MASK_IEN1); + calculate_target_cycle(m68k); + break; + case GA_CDD_CTRL: { + cdd_run(cd, m68k->current_cycle); + uint16_t changed = cd->gate_array[reg] ^ value; + cd->gate_array[reg] &= ~BIT_HOCK; + cd->gate_array[reg] |= value & BIT_HOCK; + if (changed & BIT_HOCK) { + if (value & BIT_HOCK) { + cdd_hock_enabled(&cd->cdd); + } else { + cdd_hock_disabled(&cd->cdd); + } + calculate_target_cycle(m68k); + } + break; + } + case GA_CDD_CMD0: + case GA_CDD_CMD1: + case GA_CDD_CMD2: + case GA_CDD_CMD3: + cdd_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x0F0F; + break; + case GA_CDD_CMD4: + cdd_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x0F0F; + cdd_mcu_start_cmd_recv(&cd->cdd, cd->gate_array + GA_CDD_CTRL); + break; + case GA_FONT_COLOR: + cd->gate_array[reg] = value & 0xFF; + break; + case GA_FONT_BITS: + cd->gate_array[reg] = value; + break; + case GA_STAMP_SIZE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] &= BIT_GRON; + cd->gate_array[reg] |= value & (BIT_SMS|BIT_STS|BIT_RPT); + break; + case GA_STAMP_MAP_BASE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFE0; + break; + case GA_IMAGE_BUFFER_VCELLS: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x1F; + break; + case GA_IMAGE_BUFFER_START: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFF8; + break; + case GA_IMAGE_BUFFER_OFFSET: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x3F; + break; + case GA_IMAGE_BUFFER_HDOTS: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x1FF; + break; + case GA_IMAGE_BUFFER_LINES: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFF; + break; + case GA_TRACE_VECTOR_BASE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFFE; + cd_graphics_start(cd); + break; + default: + printf("Unhandled gate array write %X:%X\n", address, value); + } + return vcontext; +} + +static void *sub_gate_write8(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + segacd_context *cd = m68k->system; + uint32_t reg = (address & 0x1FF) >> 1; + uint16_t value16; + switch (address >> 1) + { + case GA_CDC_HOST_DATA: + case GA_CDC_DMA_ADDR: + case GA_STOP_WATCH: + case GA_COMM_FLAG: + case GA_TIMER: + case GA_CDD_FADER: + case GA_FONT_COLOR: + //these registers treat all writes as word-wide + value16 = value | (value << 8); + break; + case GA_CDC_CTRL: + if (address & 1) { + lc8951_ar_write(&cd->cdc, value); + return vcontext; + } else { + value16 = cd->cdc.ar | (value << 8); + } + break; + case GA_CDD_CMD4: + if (!address) { + //byte write to $FF804A should not trigger transfer + cdd_run(cd, m68k->current_cycle); + cd->gate_array[reg] &= 0x0F; + cd->gate_array[reg] |= (value << 8 & 0x0F00); + return vcontext; + } + //intentional fallthrough for $FF804B + default: + if (address & 1) { + value16 = cd->gate_array[reg] & 0xFF00 | value; + } else { + value16 = cd->gate_array[reg] & 0xFF | (value << 8); + } + } + return sub_gate_write16(address, vcontext, value16); +} + +static uint8_t handle_cdc_byte(void *vsys, uint8_t value) +{ + segacd_context *cd = vsys; + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + //host reg is already full, pause transfer + return 0; + } + if (cd->cdc.cycle == cd->cdc.transfer_end) { + cd->gate_array[GA_CDC_CTRL] |= BIT_EDT; + printf("EDT set at %u\n", cd->cdc.cycle); + } + uint16_t dest = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + if (!(cd->cdc_dst_low & 1)) { + cd->gate_array[GA_CDC_HOST_DATA] &= 0xFF; + cd->gate_array[GA_CDC_HOST_DATA] |= value << 8; + cd->cdc_dst_low++; + if (dest != DST_PCM_RAM) { + //PCM RAM writes a byte at a time + return 1; + } + } else { + cd->gate_array[GA_CDC_HOST_DATA] &= 0xFF00; + cd->gate_array[GA_CDC_HOST_DATA] |= value; + } + + uint32_t dma_addr = cd->gate_array[GA_CDC_DMA_ADDR] << 3; + dma_addr |= cd->cdc_dst_low; + switch (dest) + { + case DST_MAIN_CPU: + case DST_SUB_CPU: + cd->cdc_dst_low = 0; + cd->gate_array[GA_CDC_CTRL] |= BIT_DSR; + printf("DSR set at %u, (transfer_end %u, dbcl %X, dbch %X)\n", cd->cdc.cycle, cd->cdc.transfer_end, cd->cdc.regs[2], cd->cdc.regs[3]); + break; + case DST_PCM_RAM: + dma_addr &= (1 << 13) - 1; + //TODO: write to currently visible 8K bank of PCM RAM I guess? + dma_addr += 2; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + case DST_PROG_RAM: + cd->prog_ram[dma_addr >> 1] = cd->gate_array[GA_CDC_HOST_DATA]; + m68k_invalidate_code_range(cd->m68k, dma_addr - 1, dma_addr + 1); + dma_addr++; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + case DST_WORD_RAM: + if (cd->gate_array[GA_MEM_MODE] & BIT_MEM_MODE) { + //1M mode, write to bank assigned to Sub CPU + dma_addr &= (1 << 17) - 1; + cd->m68k->mem_pointers[1][dma_addr >> 1] = cd->gate_array[GA_CDC_HOST_DATA]; + m68k_invalidate_code_range(cd->m68k, 0x0C0000 + dma_addr - 1, 0x0C0000 + dma_addr + 1); + } else { + //2M mode, check if Sub CPU has access + if (!(cd->gate_array[GA_MEM_MODE] & BIT_RET)) { + dma_addr &= (1 << 18) - 1; + cd->word_ram[dma_addr >> 1] = cd->gate_array[GA_CDC_HOST_DATA]; + m68k_invalidate_code_range(cd->m68k, 0x080000 + dma_addr, 0x080000 + dma_addr + 1); + } + } + dma_addr++; + cd->cdc_dst_low = dma_addr & 7; + cd->gate_array[GA_CDC_DMA_ADDR] = dma_addr >> 3; + break; + default: + printf("Invalid CDC transfer destination %d\n", dest); + } + return 1; +} + +static uint8_t can_main_access_prog(segacd_context *cd) +{ + //TODO: use actual busack + return cd->busreq || !cd->reset; +} + +static void scd_peripherals_run(segacd_context *cd, uint32_t cycle) +{ + timers_run(cd, cycle); + cdd_run(cd, cycle); + cd_graphics_run(cd, cycle); +} + +static m68k_context *sync_components(m68k_context * context, uint32_t address) +{ + segacd_context *cd = context->system; + scd_peripherals_run(cd, context->current_cycle); + switch (context->int_ack) + { + case 1: + cd->graphics_int_cycle = CYCLE_NEVER; + break; + case 2: + cd->int2_cycle = CYCLE_NEVER; + break; + case 3: + cd->timer_pending = 0; + break; + case 4: + cd->cdd.int_pending = 0; + break; + case 5: + cd->cdc_int_ack = 1; + break; + } + context->int_ack = 0; + calculate_target_cycle(context); + return context; +} + +void scd_run(segacd_context *cd, uint32_t cycle) +{ + uint8_t m68k_run = !can_main_access_prog(cd); + if (cycle > cd->m68k->current_cycle) { + if (m68k_run) { + uint32_t start = cd->m68k->current_cycle; + cd->m68k->sync_cycle = cycle; + if (cd->need_reset) { + cd->need_reset = 0; + m68k_reset(cd->m68k); + } else { + calculate_target_cycle(cd->m68k); + resume_68k(cd->m68k); + } + } else { + cd->m68k->current_cycle = cycle; + } + } + scd_peripherals_run(cd, cycle); +} + +uint32_t gen_cycle_to_scd(uint32_t cycle, genesis_context *gen) +{ + return ((uint64_t)cycle) * ((uint64_t)SCD_MCLKS) / ((uint64_t)gen->normal_clock); +} + +void scd_adjust_cycle(segacd_context *cd, uint32_t deduction) +{ + deduction = gen_cycle_to_scd(deduction, cd->genesis); + cd->m68k->current_cycle -= deduction; + cd->stopwatch_cycle -= deduction; + if (deduction >= cd->int2_cycle) { + cd->int2_cycle = 0; + } else if (cd->int2_cycle != CYCLE_NEVER) { + cd->int2_cycle -= deduction; + } + if (deduction >= cd->periph_reset_cycle) { + cd->periph_reset_cycle = CYCLE_NEVER; + } else if (cd->periph_reset_cycle != CYCLE_NEVER) { + cd->periph_reset_cycle -= deduction; + } + cdd_mcu_adjust_cycle(&cd->cdd, deduction); + lc8951_adjust_cycles(&cd->cdc, deduction); + cd->graphics_cycle -= deduction; + if (cd->graphics_int_cycle != CYCLE_NEVER) { + if (cd->graphics_int_cycle > deduction) { + cd->graphics_int_cycle -= deduction; + } else { + cd->graphics_int_cycle = 0; + } + } +} + +static uint16_t main_gate_read16(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + segacd_context *cd = gen->expansion; + uint32_t scd_cycle = gen_cycle_to_scd(m68k->current_cycle, gen); + scd_run(cd, scd_cycle); + uint32_t offset = (address & 0x1FF) >> 1; + switch (offset) + { + case GA_SUB_CPU_CTRL: { + uint16_t value = 0; + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2) { + value |= BIT_IEN2; + } + if (cd->int2_cycle != CYCLE_NEVER) { + value |= BIT_IFL2; + } + if (can_main_access_prog(cd)) { + value |= BIT_SBRQ; + } + if (cd->reset) { + value |= BIT_SRES; + } + return value; + } + case GA_MEM_MODE: + //Main CPU can't read priority mode bits + return cd->gate_array[offset] & 0xFFE7; + case GA_HINT_VECTOR: + return cd->rom_mut[0x72/2]; + case GA_CDC_HOST_DATA: { + uint16_t dst = cd->gate_array[GA_CDC_CTRL] >> 8 & 0x7; + if (dst == DST_MAIN_CPU) { + if (cd->gate_array[GA_CDC_CTRL] & BIT_DSR) { + printf("DSR cleared at %u (%u)\n", scd_cycle, cd->cdc.cycle); + cd->gate_array[GA_CDC_CTRL] &= ~BIT_DSR; + lc8951_resume_transfer(&cd->cdc, scd_cycle); + } else { + printf("Read of CDC host data with DSR clear at %u\n", scd_cycle); + } + calculate_target_cycle(cd->m68k); + } + return cd->gate_array[offset]; + } + case GA_CDC_DMA_ADDR: + //TODO: open bus maybe? + return 0xFFFF; + default: + if (offset < GA_TIMER) { + if (offset == GA_CDC_CTRL) { + printf("CDC read(main): %X - %X @ %u (%u)\n", address, cd->gate_array[offset], m68k->current_cycle, scd_cycle); + } else if (offset >= GA_COMM_FLAG && offset <= GA_COMM_STATUS7) { + printf("COMM read(main): %X - %X @ %u (%u)\n", address, cd->gate_array[offset], m68k->current_cycle, scd_cycle); + } + return cd->gate_array[offset]; + } + //TODO: open bus maybe? + return 0xFFFF; + } +} + +static uint8_t main_gate_read8(uint32_t address, void *vcontext) +{ + uint16_t val = main_gate_read16(address & 0xFE, vcontext); + return address & 1 ? val : val >> 8; +} + +static void dump_prog_ram(segacd_context *cd) +{ + static int dump_count; + char fname[256]; + sprintf(fname, "prog_ram_%d.bin", dump_count++); + FILE *f = fopen(fname, "wb"); + if (f) { + uint32_t last = 256*1024-1; + for(; last > 0; --last) + { + if (cd->prog_ram[last]) { + break; + } + } + for (uint32_t i = 0; i <= last; i++) + { + uint8_t pair[2]; + pair[0] = cd->prog_ram[i] >> 8; + pair[1] = cd->prog_ram[i]; + fwrite(pair, 1, sizeof(pair), f); + } + + fclose(f); + } +} + +static void *main_gate_write16(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + segacd_context *cd = gen->expansion; + uint32_t scd_cycle = gen_cycle_to_scd(m68k->current_cycle, gen); + scd_run(cd, scd_cycle); + uint32_t reg = (address & 0x1FF) >> 1; + switch (reg) + { + case GA_SUB_CPU_CTRL: { + uint8_t old_access = can_main_access_prog(cd); + cd->busreq = value & BIT_SBRQ; + uint8_t old_reset = cd->reset; + cd->reset = value & BIT_SRES; + if (cd->reset && !old_reset) { + cd->need_reset = 1; + } + if (value & BIT_IFL2) { + cd->int2_cycle = scd_cycle; + } + /*cd->gate_array[reg] &= 0x7FFF; + cd->gate_array[reg] |= value & 0x8000;*/ + uint8_t new_access = can_main_access_prog(cd); + uint32_t bank = cd->gate_array[GA_MEM_MODE] >> 6 & 0x3; + if (new_access) { + if (!old_access) { + m68k->mem_pointers[cd->memptr_start_index] = cd->prog_ram + bank * 0x10000; + m68k_invalidate_code_range(m68k, cd->base + 0x220000, cd->base + 0x240000); + } + } else if (old_access) { + m68k->mem_pointers[cd->memptr_start_index] = NULL; + m68k_invalidate_code_range(m68k, cd->base + 0x220000, cd->base + 0x240000); + m68k_invalidate_code_range(cd->m68k, bank * 0x20000, (bank + 1) * 0x20000); + if (!new_access) { + dump_prog_ram(cd); + } + } + break; + } + case GA_MEM_MODE: { + uint16_t changed = cd->gate_array[reg] ^ value; + //Main CPU can't write priority mode bits, MODE or RET + cd->gate_array[reg] &= 0x001F; + cd->gate_array[reg] |= value & 0xFFC0; + if ((cd->gate_array[reg] & BIT_MEM_MODE)) { + //1M mode + if (!(value & BIT_DMNA)) { + cd->gate_array[reg] |= BIT_DMNA; + } + } else { + //2M mode + if (changed & value & BIT_DMNA) { + cd->gate_array[reg] |= BIT_DMNA; + m68k->mem_pointers[cd->memptr_start_index + 1] = NULL; + m68k->mem_pointers[cd->memptr_start_index + 2] = NULL; + cd->m68k->mem_pointers[0] = cd->word_ram; + cd->gate_array[reg] &= ~BIT_RET; + + m68k_invalidate_code_range(m68k, cd->base + 0x200000, cd->base + 0x240000); + m68k_invalidate_code_range(cd->m68k, 0x080000, 0x0C0000); + } + } + if (changed & MASK_PROG_BANK && can_main_access_prog(cd)) { + uint32_t bank = cd->gate_array[GA_MEM_MODE] >> 6 & 0x3; + m68k->mem_pointers[cd->memptr_start_index] = cd->prog_ram + bank * 0x10000; + m68k_invalidate_code_range(m68k, cd->base + 0x220000, cd->base + 0x240000); + } + break; + } + case GA_HINT_VECTOR: + cd->rom_mut[0x72/2] = value; + break; + case GA_COMM_FLAG: + //Main CPU can only write the upper byte; + cd->gate_array[reg] &= 0xFF; + cd->gate_array[reg] |= value & 0xFF00; + printf("COMM write(main): %X - %X @ %u (%u)\n", address, value, m68k->current_cycle, scd_cycle); + break; + case GA_COMM_CMD0: + case GA_COMM_CMD1: + case GA_COMM_CMD2: + case GA_COMM_CMD3: + case GA_COMM_CMD4: + case GA_COMM_CMD5: + case GA_COMM_CMD6: + case GA_COMM_CMD7: + //no effects for these other than saving the value + printf("COMM write(main): %X - %X @ %u (%u)\n", address, value, m68k->current_cycle, scd_cycle); + cd->gate_array[reg] = value; + break; + default: + printf("Unhandled gate array write %X:%X\n", address, value); + } + return vcontext; +} + +static void *main_gate_write8(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + segacd_context *cd = gen->expansion; + uint32_t reg = (address & 0x1FF) >> 1; + uint16_t value16; + switch (reg >> 1) + { + case GA_SUB_CPU_CTRL: + if (address & 1) { + value16 = value; + } else { + value16 = value << 8; + if (cd->reset) { + value16 |= BIT_SRES; + } + if (cd->busreq) { + value16 |= BIT_SBRQ; + } + } + break; + case GA_HINT_VECTOR: + case GA_COMM_FLAG: + //writes to these regs are always treated as word wide + value16 = value | (value << 8); + break; + default: + if (address & 1) { + value16 = cd->gate_array[reg] & 0xFF00 | value; + } else { + value16 = cd->gate_array[reg] & 0xFF | (value << 8); + } + } + return main_gate_write16(address, vcontext, value16); +} + +segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info) +{ + static memmap_chunk sub_cpu_map[] = { + {0x000000, 0x01FEFF, 0xFFFFFF, .flags=MMAP_READ | MMAP_CODE, .write_16 = prog_ram_wp_write16, .write_8 = prog_ram_wp_write8}, + {0x01FF00, 0x07FFFF, 0xFFFFFF, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE}, + {0x080000, 0x0BFFFF, 0x03FFFF, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE | MMAP_PTR_IDX | MMAP_FUNC_NULL, .ptr_index = 0, + .read_16 = word_ram_2M_read16, .write_16 = word_ram_2M_write16, .read_8 = word_ram_2M_read8, .write_8 = word_ram_2M_write8}, + {0x0C0000, 0x0DFFFF, 0x01FFFF, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE | MMAP_PTR_IDX | MMAP_FUNC_NULL, .ptr_index = 1, + .read_16 = word_ram_1M_read16, .write_16 = word_ram_1M_write16, .read_8 = word_ram_1M_read8, .write_8 = word_ram_1M_write8}, + {0xFE0000, 0xFEFFFF, 0x003FFF, .flags=MMAP_READ | MMAP_WRITE | MMAP_ONLY_ODD}, + {0xFF0000, 0xFF7FFF, 0x003FFF, .read_16 = pcm_read16, .write_16 = pcm_write16, .read_8 = pcm_read8, .write_8 = pcm_write8}, + {0xFF8000, 0xFF81FF, 0x0001FF, .read_16 = sub_gate_read16, .write_16 = sub_gate_write16, .read_8 = sub_gate_read8, .write_8 = sub_gate_write8} + }; + + segacd_context *cd = calloc(sizeof(segacd_context), 1); + FILE *f = fopen("cdbios.bin", "rb"); + if (!f) { + fatal_error("Failed to open CD firmware for reading"); + } + long firmware_size = file_size(f); + uint32_t adjusted_size = nearest_pow2(firmware_size); + cd->rom = malloc(adjusted_size); + if (firmware_size != fread(cd->rom, 1, firmware_size, f)) { + fatal_error("Failed to read CD firmware"); + } + cd->rom_mut = malloc(adjusted_size); + byteswap_rom(adjusted_size, cd->rom); + memcpy(cd->rom_mut, cd->rom, adjusted_size); + cd->rom_mut[0x72/2] = 0xFFFF; + + //memset(info, 0, sizeof(*info)); + //tern_node *db = get_rom_db(); + //*info = configure_rom(db, media->buffer, media->size, media->chain ? media->chain->buffer : NULL, media->chain ? media->chain->size : 0, NULL, 0); + + cd->prog_ram = malloc(512*1024); + cd->word_ram = malloc(256*1024); + cd->pcm_ram = malloc(64*1024); + //TODO: Load state from file + cd->bram = malloc(8*1024); + + + sub_cpu_map[0].buffer = sub_cpu_map[1].buffer = cd->prog_ram; + sub_cpu_map[4].buffer = cd->bram; + m68k_options *mopts = malloc(sizeof(m68k_options)); + init_m68k_opts(mopts, sub_cpu_map, sizeof(sub_cpu_map) / sizeof(*sub_cpu_map), 4, sync_components); + cd->m68k = init_68k_context(mopts, NULL); + cd->m68k->system = cd; + cd->int2_cycle = CYCLE_NEVER; + cd->busreq = 1; + cd->busack = 1; + cd->need_reset = 1; + cd->reset = 1; //active low, so reset is not active on start + cd->memptr_start_index = 0; + cd->gate_array[1] = 1; + cd->gate_array[0x1B] = 0x100; + lc8951_init(&cd->cdc, handle_cdc_byte, cd); + if (media->chain && media->type != MEDIA_CDROM) { + media = media->chain; + } + cdd_mcu_init(&cd->cdd, media); + cd_graphics_init(cd); + + return cd; +} + +memmap_chunk *segacd_main_cpu_map(segacd_context *cd, uint8_t cart_boot, uint32_t *num_chunks) +{ + static memmap_chunk main_cpu_map[] = { + {0x000000, 0x01FFFF, 0x01FFFF, .flags=MMAP_READ}, + {0x020000, 0x03FFFF, 0x01FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_CODE, .ptr_index = 0, + .read_16 = unmapped_prog_read16, .write_16 = unmapped_prog_write16, .read_8 = unmapped_prog_read8, .write_8 = unmapped_prog_write8}, + {0x040000, 0x05FFFF, 0x01FFFF, .flags=MMAP_READ}, //first ROM alias + //TODO: additional ROM/prog RAM aliases + {0x200000, 0x21FFFF, 0x01FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_CODE, .ptr_index = 1, + .read_16 = unmapped_word_read16, .write_16 = unmapped_word_write16, .read_8 = unmapped_word_read8, .write_8 = unmapped_word_write8}, + {0x220000, 0x23FFFF, 0x01FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_CODE, .ptr_index = 2, + .read_16 = cell_image_read16, .write_16 = cell_image_write16, .read_8 = cell_image_read8, .write_8 = cell_image_write8}, + {0xA12000, 0xA12FFF, 0xFFFFFF, .read_16 = main_gate_read16, .write_16 = main_gate_write16, .read_8 = main_gate_read8, .write_8 = main_gate_write8} + }; + for (int i = 0; i < sizeof(main_cpu_map) / sizeof(*main_cpu_map); i++) + { + if (main_cpu_map[i].start < 0x800000) { + if (cart_boot) { + main_cpu_map[i].start |= 0x400000; + main_cpu_map[i].end |= 0x400000; + } else { + main_cpu_map[i].start &= 0x3FFFFF; + main_cpu_map[i].end &= 0x3FFFFF; + } + } + } + //TODO: support BRAM cart + main_cpu_map[0].buffer = cd->rom_mut; + main_cpu_map[2].buffer = cd->rom; + main_cpu_map[1].buffer = cd->prog_ram; + main_cpu_map[3].buffer = cd->word_ram; + main_cpu_map[4].buffer = cd->word_ram + 0x10000; + *num_chunks = sizeof(main_cpu_map) / sizeof(*main_cpu_map); + return main_cpu_map; +} diff -r 3748a2a8a4b7 -r cc13c100b027 segacd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/segacd.h Sun Jan 30 22:29:29 2022 -0800 @@ -0,0 +1,52 @@ +#ifndef SEGACD_H_ +#define SEGACD_H_ +#include +#include "genesis.h" +#include "lc8951.h" +#include "cdd_mcu.h" + +typedef struct { + m68k_context *m68k; + system_media *media; + genesis_context *genesis; + uint16_t gate_array[0x100]; + uint16_t *rom; //unaltered ROM, needed for mirrored locations + uint16_t *rom_mut; //ROM with low 16-bit of HINT vector modified by register write + uint16_t *prog_ram; + uint16_t *word_ram; + uint8_t *pcm_ram; + uint8_t *bram; + uint32_t stopwatch_cycle; + uint32_t int2_cycle; + uint32_t graphics_int_cycle; + uint32_t periph_reset_cycle; + uint32_t graphics_cycle; + uint32_t base; + uint32_t graphics_x; + uint32_t graphics_y; + uint32_t graphics_dx; + uint32_t graphics_dy; + uint16_t graphics_dst_x; + uint8_t graphics_pixels[4]; + uint8_t timer_pending; + uint8_t timer_value; + uint8_t busreq; + uint8_t busack; + uint8_t reset; + uint8_t need_reset; + uint8_t memptr_start_index; + lc8951 cdc; + cdd_mcu cdd; + uint8_t cdc_dst_low; + uint8_t cdc_int_ack; + uint8_t graphics_step; + uint8_t graphics_dst_y; +} segacd_context; + +segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info); +memmap_chunk *segacd_main_cpu_map(segacd_context *cd, uint8_t cart_boot, uint32_t *num_chunks); +uint32_t gen_cycle_to_scd(uint32_t cycle, genesis_context *gen); +void scd_run(segacd_context *cd, uint32_t cycle); +void scd_adjust_cycle(segacd_context *cd, uint32_t deduction); + +#endif //SEGACD_H_ diff -r 3748a2a8a4b7 -r cc13c100b027 sf2_50mhz_analysis.txt --- a/sf2_50mhz_analysis.txt Sat Jan 01 18:54:46 2022 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ -HSYNC End -Plane A: D13C -??slot?? -tile -tile -Plane B: F29C -?sprite tile slot? -tile -tile -Plane A: D100 -68K Slot -tile -tile -Plane B: F2A0 -?sprite table slot? -tile -tile -Plane A: D104 -68K slot -tile -tile -Plane B: F2A4 -?sprite table slot? -tile -tile -Plane A: D108 -68K slot -?tile? -?tile? -Plane B: F2A8 -?sprite table slot? -tile -tile -Plane A: D10C -refresh -?tile? -?tile? -Plane B: F22C -?sprite table slot? -tile -tile -Plane A: D110 -68K slot -?tile? -?tile? -Plane B: F230 -?sprite table slot? -tile -tile -Plane A: D114 -68K slot -?tile? -?tile? -Plane B: F2F4 -?sprite table slot? -tile -tile -Plane A: D118 -68K slot -?tile? -?tile? -Plane B: F238 -?sprite table slot? -ttile tile -Plane A: D11C -refresh -?tile? -?tile? -Plane B: F23C -?sprite table slot? -tile -tile -Plane A: D120 -68K slot -?tile? -?tile? -Plane B: F240 -?sprite table slot? -tile -tile -Plane A: D124 -68K Slot -?tile? -?tile? -Plane B: F244 -?sprite table slot? -tile -tile -Plane A: D128 -68K Slot -?tile? -?tile? -Plane B: F3C8 -?sprite table slot? -tile -tile -Plane A: D12C -refresh -?tile? -?tile? -Plane B: F24C -?sprite table slot? -tile -tile -Plane A: D130 -68K slot -?tile? -?tile? -Plane B: F250 -?sprite table slot? -tile -tile -Plane A: D134 -68K Slot -?tile? -?tile? -Plane B: F254 -?sprite table slot? -tile -tile -Plane A: D138 -68K slot -?tile? -?tile? -Plane B: F3D8 -?sprite table slot? -tile -tile -Plane A: D13C -refresh -tile -tile -Plane B: F3DC -?sprite table slot? -tile -tile ---------------- -68K Slot -68K Slot -sprite tile slot -sprite tile slot -sprite tile slot -sprite tile slot -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -68K Slot -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -HSYNC Start -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?unkown tile slot? -68K Slot -Horizontal Scroll -?sprite tile slot? -?sprite tile slot? -?sprite tile slot? -?unkown tile slot? diff -r 3748a2a8a4b7 -r cc13c100b027 sf2_vram_map.txt --- a/sf2_vram_map.txt Sat Jan 01 18:54:46 2022 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -0000: single color blocks -0200: Random symbols???? -0400-0C00: Font -0C00-0E??: Score font -0E??: Player name displays - -2000-3000: floor (mostly, some garbage around 2800 and 2A00) -3000-6C00: background -6C00-8000: not graphics -8000-~8A00: bat/bird sprites, special attack text -8A00-9000: ????? -9000-9200: More special attack text -9200-9600: another font -9800-AA00: special attack effects? -AC00-B000: ???? -B000-B500: character sprite -???? -C000-C500: chracter sprite -D000-DA00: window name table -DA00-DC00: Sprite attribute table -DC00-E000: horizontal scroll data -E000-????: plane A&B name table -C500-FFFF: not graphics - -VDP Registers: -Mode -00 - 14 -01 - 64 -Scroll A Name Table Address: E000 -02 - 38 -Window Name Table Address: D000 -03 - 34 -Scroll B Name Table Address: E000 -04 - 07 -Sprite Attribute Table Address: DA00 -05 - 6D -06 - 00 -Backdrop color - 0 -07 - 00 -08 - 00 -09 - 00 -HINT Counter -0A - AF -Mode - Full screen vertical scroll, line horizontal scroll, external ints disabled, 32 cell display, no interlacing -0B - 03 -0C - 00 -H Scroll Data Address: DC00 -0D - 37 -0E - 00 -Auto increment -0F - 02 -Scroll Size -10 - 11 -Window H Pos -11 - 00 -Window V Pos -12 - 05 -DMA transfer length -13 - 00 -14 - 00 -DMA source address and mode -15 - DB -16 - CF -17 - 7F diff -r 3748a2a8a4b7 -r cc13c100b027 system.c --- a/system.c Sat Jan 01 18:54:46 2022 -0800 +++ b/system.c Sun Jan 30 22:29:29 2022 -0800 @@ -13,7 +13,11 @@ system_type detect_system_type(system_media *media) { if (safe_cmp("SEGA", 0x100, media->buffer, media->size)) { - //TODO: Differentiate between vanilla Genesis and Sega CD/32X games + //TODO: support other bootable identifiers + if (safe_cmp("SEGADISCSYSTEM", 0, media->buffer, media->size)) { + return SYSTEM_SEGACD; + } + //TODO: Differentiate between vanilla Genesis and 32X games return SYSTEM_GENESIS; } if (safe_cmp("TMR SEGA", 0x1FF0, media->buffer, media->size) @@ -71,6 +75,8 @@ return &(alloc_config_genesis(media->buffer, media->size, lock_on, lock_on_size, opts, force_region))->header; case SYSTEM_GENESIS_PLAYER: return &(alloc_config_gen_player(media->buffer, media->size))->header; + case SYSTEM_SEGACD: + return &(alloc_config_genesis_cdboot(media, opts, force_region))->header; #ifndef NO_Z80 case SYSTEM_SMS: return &(alloc_configure_sms(media, opts, force_region))->header; diff -r 3748a2a8a4b7 -r cc13c100b027 system.h --- a/system.h Sat Jan 01 18:54:46 2022 -0800 +++ b/system.h Sun Jan 30 22:29:29 2022 -0800 @@ -2,6 +2,7 @@ #define SYSTEM_H_ #include #include +#include typedef struct system_header system_header; typedef struct system_media system_media; @@ -10,6 +11,7 @@ SYSTEM_UNKNOWN, SYSTEM_GENESIS, SYSTEM_GENESIS_PLAYER, + SYSTEM_SEGACD, SYSTEM_SMS, SYSTEM_SMS_PLAYER, SYSTEM_JAGUAR, @@ -38,53 +40,75 @@ #include "event_log.h" struct system_header { - system_header *next_context; - system_str_fun start_context; - system_fun resume_context; - system_fun load_save; - system_fun persist_save; - system_u8_fun_r8 load_state; - system_fun request_exit; - system_fun soft_reset; - system_fun free_context; - system_fun_r16 get_open_bus_value; - system_u32_fun set_speed_percent; - system_fun inc_debug_mode; - system_u8_u8_fun gamepad_down; - system_u8_u8_fun gamepad_up; - system_u8_u8_fun mouse_down; - system_u8_u8_fun mouse_up; - system_mabs_fun mouse_motion_absolute; - system_mrel_fun mouse_motion_relative; - system_u8_fun keyboard_down; - system_u8_fun keyboard_up; - system_fun config_updated; + system_header *next_context; + system_str_fun start_context; + system_fun resume_context; + system_fun load_save; + system_fun persist_save; + system_u8_fun_r8 load_state; + system_fun request_exit; + system_fun soft_reset; + system_fun free_context; + system_fun_r16 get_open_bus_value; + system_u32_fun set_speed_percent; + system_fun inc_debug_mode; + system_u8_u8_fun gamepad_down; + system_u8_u8_fun gamepad_up; + system_u8_u8_fun mouse_down; + system_u8_u8_fun mouse_up; + system_mabs_fun mouse_motion_absolute; + system_mrel_fun mouse_motion_relative; + system_u8_fun keyboard_down; + system_u8_fun keyboard_up; + system_fun config_updated; system_ptrszt_fun_rptr8 serialize; system_ptr8_sizet_fun deserialize; system_str_fun start_vgm_log; system_fun stop_vgm_log; - rom_info info; - arena *arena; - char *next_rom; - char *save_dir; - uint8_t enter_debugger; - uint8_t should_exit; - uint8_t save_state; - uint8_t delayed_load_slot; - uint8_t has_keyboard; + rom_info info; + arena *arena; + char *next_rom; + char *save_dir; + uint8_t enter_debugger; + uint8_t should_exit; + uint8_t save_state; + uint8_t delayed_load_slot; + uint8_t has_keyboard; uint8_t vgm_logging; uint8_t force_release; - debugger_type debugger_type; - system_type type; + debugger_type debugger_type; + system_type type; }; +typedef enum { + MEDIA_CART, + MEDIA_CDROM +} media_type; + +typedef enum { + TRACK_AUDIO, + TRACK_DATA +} track_type; + +typedef struct { + uint32_t fake_pregap; + uint32_t pregap_lba; + uint32_t start_lba; + uint32_t end_lba; + track_type type; +} track_info; + struct system_media { void *buffer; char *dir; char *name; char *extension; system_media *chain; + track_info *tracks; + FILE *f; + uint32_t num_tracks; uint32_t size; + media_type type; }; #define OPT_ADDRESS_LOG (1U << 31U)