changeset 2054:8ee7ecbf3f21 segacd

Implement enough of Sega CD gate array and Sub CPU to pass Sik's Mode 1 test ROM
author Michael Pavone <pavone@retrodev.com>
date Tue, 18 Jan 2022 00:03:50 -0800
parents 3414a4423de1
children c4d066d798c4
files backend_x86.c debug.c genesis.c genesis.h m68k_core.c m68k_core.h m68k_core_x86.c m68k_internal.h romdb.c romdb.h segacd.c segacd.h
diffstat 12 files changed, 815 insertions(+), 284 deletions(-) [+]
line wrap: on
line diff
--- a/backend_x86.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/backend_x86.c	Tue Jan 18 00:03:50 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;
 		}
@@ -312,13 +324,35 @@
 			}
 		}
 		if (lb_jcc) {
-			*lb_jcc = code->cur - (lb_jcc+1);
+			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) {
-			*ub_jcc = code->cur - (ub_jcc+1);
+			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 (need_wide_jcc) {
+			need_wide_jcc = 0;
+		}
 	}
 	if (!is_write) {
 		mov_ir(code, size == SZ_B ? 0xFF : 0xFFFF, opts->scratch1, size);
--- a/debug.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/debug.c	Tue Jan 18 00:03:50 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
--- a/genesis.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/genesis.c	Tue Jan 18 00:03:50 2022 -0800
@@ -247,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)
@@ -413,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;
@@ -434,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;
@@ -471,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) {
@@ -1878,35 +1893,68 @@
 	return gen;
 }
 
-genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on, 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}
+};
+const size_t base_chunks = sizeof(base_map)/sizeof(*base_map);
+
+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 *gen = shared_init(system_opts, rom, force_region);
-	gen->z80->mem_pointers[1] = gen->z80->mem_pointers[2] = (uint8_t *)main_rom;
+	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 = main_rom;
+	gen->cart = rom;
 	gen->lock_on = lock_on;
 
-	setup_io_devices(config, rom, &gen->io);
+	setup_io_devices(config, &info, &gen->io);
 	gen->header.has_keyboard = io_has_keyboard(&gen->io);
-	gen->mapper_type = rom->mapper_type;
-	gen->save_type = rom->save_type;
+	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");
@@ -1914,19 +1962,19 @@
 	//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;
 			}
@@ -1965,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;
 		}
 	}
 
@@ -2079,44 +2158,6 @@
 	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);
-
-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;
-	}
-	return alloc_init_genesis(&info, rom, lock_on, ym_opts, force_region);
-}
-
 genesis_context *alloc_config_genesis_cdboot(system_media *media, uint32_t system_opts, uint8_t force_region)
 {
 	tern_node *rom_db = get_rom_db();
@@ -2130,10 +2171,12 @@
 	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, &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);
@@ -2141,7 +2184,7 @@
 	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);
+	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);
--- a/genesis.h	Sat Jan 15 13:15:21 2022 -0800
+++ b/genesis.h	Tue Jan 18 00:03:50 2022 -0800
@@ -81,7 +81,6 @@
 #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);
--- a/m68k_core.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/m68k_core.c	Tue Jan 18 00:03:50 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");
 	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);
 }
 
--- a/m68k_core.h	Sat Jan 15 13:15:21 2022 -0800
+++ b/m68k_core.h	Tue Jan 18 00:03:50 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);
--- a/m68k_core_x86.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/m68k_core_x86.c	Tue Jan 18 00:03:50 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);
 }
--- a/m68k_internal.h	Sat Jan 15 13:15:21 2022 -0800
+++ b/m68k_internal.h	Tue Jan 18 00:03:50 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();
--- a/romdb.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/romdb.c	Tue Jan 18 00:03:50 2022 -0800
@@ -475,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;
 }
 
@@ -1055,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;
 }
--- a/romdb.h	Sat Jan 15 13:15:21 2022 -0800
+++ b/romdb.h	Tue Jan 18 00:03:50 2022 -0800
@@ -79,6 +79,7 @@
 	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
--- a/segacd.c	Sat Jan 15 13:15:21 2022 -0800
+++ b/segacd.c	Tue Jan 18 00:03:50 2022 -0800
@@ -4,52 +4,143 @@
 #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_HINT_VECTOR = GA_CDC_REG_DATA
+};
+//GA_SUB_CPU_CTRL
+#define BIT_IEN2       0x8000
+#define BIT_IFL2       0x0100
+#define BIT_LEDG       0x0100
+#define BIT_LEDR       0x0080
+#define BIT_SBRQ       0x0002
+#define BIT_SRES       0x0001
+#define BIT_PRES       0x0001
+//GA_MEM_MODE
+#define MASK_PROG_BANK 0x00C0
+#define MASK_PRIORITY  0x0018
+#define BIT_MEM_MODE   0x0004
+#define BIT_DMNA       0x0002
+#define BIT_RET        0x0001
+//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
+
 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 >> 17) + 8)))) {
+		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 (!(cd->gate_array[GA_MEM_MODE] & (1 << ((address >> 17) + 8)))) {
+		((uint8_t *)cd->prog_ram)[address ^ 1] = value;
+		m68k_invalidate_code_range(m68k, address, address + 1);
+	}
 	return vcontext;
 }
 
-static uint16_t work_ram_2M_read16(uint32_t address, void *vcontext)
+static uint16_t word_ram_2M_read16(uint32_t address, void *vcontext)
 {
 	return 0;
 }
 
-static uint8_t work_ram_2M_read8(uint32_t address, void *vcontext)
+static uint8_t word_ram_2M_read8(uint32_t address, void *vcontext)
 {
 	return 0;
 }
 
-static void *work_ram_2M_write16(uint32_t address, void *vcontext, uint16_t value)
+static void *word_ram_2M_write16(uint32_t address, void *vcontext, uint16_t value)
+{
+	return vcontext;
+}
+
+static void *word_ram_2M_write8(uint32_t address, void *vcontext, uint8_t value)
 {
 	return vcontext;
 }
 
-static void *work_ram_2M_write8(uint32_t address, void *vcontext, uint8_t value)
+static uint16_t word_ram_1M_read16(uint32_t address, void *vcontext)
 {
-	return vcontext;
+	return 0;
 }
 
-static uint16_t work_ram_1M_read16(uint32_t address, void *vcontext)
+static uint8_t word_ram_1M_read8(uint32_t address, void *vcontext)
 {
 	return 0;
 }
 
-static uint8_t work_ram_1M_read8(uint32_t address, void *vcontext)
+static void *word_ram_1M_write16(uint32_t address, void *vcontext, uint16_t value)
 {
-	return 0;
+	return vcontext;
 }
 
-static void *work_ram_1M_write16(uint32_t address, void *vcontext, uint16_t value)
+static void *word_ram_1M_write8(uint32_t address, void *vcontext, uint8_t value)
 {
 	return vcontext;
 }
 
-static void *work_ram_1M_write8(uint32_t address, void *vcontext, uint8_t value)
+
+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;
 }
@@ -74,18 +165,108 @@
 	return pcm_write8(address+1, vcontext, value);
 }
 
+
+static void timers_run(segacd_context *cd, uint32_t cycle)
+{
+	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 (!cd->timer_value) {
+		--ticks;
+		cd->timer_value = cd->gate_array[GA_TIMER];
+	}
+	if (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 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 < 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 (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;
-	return cd->gate_array[(address & 0x1FF) >> 1];
+	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_STOP_WATCH:
+	case GA_TIMER:
+		timers_run(cd, m68k->current_cycle);
+		return cd->gate_array[reg];
+	default:
+		return cd->gate_array[reg];
+	}
 }
 
 static uint8_t sub_gate_read8(uint32_t address, void *vcontext)
 {
-	m68k_context *m68k = vcontext;
-	segacd_context *cd = m68k->system;
-	uint16_t val = cd->gate_array[(address & 0x1FF) >> 1];
+	uint16_t val = sub_gate_read16(address, vcontext);
 	return address & 1 ? val : val >> 8;
 }
 
@@ -93,24 +274,98 @@
 {
 	m68k_context *m68k = vcontext;
 	segacd_context *cd = m68k->system;
-	uint32_t reg = (address & 0x1FF) >> 1;
+	uint32_t reg = address >> 1;
 	switch (reg)
 	{
-	case 0x7:
+	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
+					genesis_context *gen = cd->genesis;
+					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) {
+			cd->gate_array[reg] &= ~BIT_DMNA;
+			if (value & BIT_MEM_MODE) {
+				//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) {
+				//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);
+			}
+		}
+		cd->gate_array[reg] &= 0xFFC2;
+		cd->gate_array[reg] |= value & (BIT_RET|BIT_MEM_MODE|MASK_PRIORITY);
+		break;
+	}
+	case GA_STOP_WATCH:
+		//docs say you should only write zero to reset
+		//unclear what happens when other values are written
+		timers_run(cd, m68k->current_cycle);
+		cd->gate_array[reg] = value & 0xFFF;
+		break;
+	case GA_COMM_FLAG:
 		cd->gate_array[reg] &= 0xFF00;
 		cd->gate_array[reg] |= value & 0xFF;
 		break;
-	case 0x10:
-	case 0x11:
-	case 0x12:
-	case 0x13:
-	case 0x14:
-	case 0x15:
-	case 0x16:
-	case 0x17:
+	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;
 	default:
 		printf("Unhandled gate array write %X:%X\n", address, value);
 	}
@@ -131,18 +386,111 @@
 	return sub_gate_write16(address, vcontext, value16);
 }
 
+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);
+}
+
+static m68k_context *sync_components(m68k_context * context, uint32_t address)
+{
+	segacd_context *cd = context->system;
+	scd_peripherals_run(cd, context->current_cycle);
+	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 (m68k_run) {
+		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)gen->normal_clock) / ((uint64_t)SCD_MCLKS);
+}
+
+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;
+	}
+}
+
 static uint16_t main_gate_read16(uint32_t address, void *vcontext)
 {
 	m68k_context *m68k = vcontext;
-	segacd_context *cd = m68k->system;
-	return cd->gate_array[(address & 0x1FF) >> 1];
+	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_DMA_ADDR:
+		//TODO: open bus maybe?
+		return 0xFFFF;
+	default:
+		if (offset < GA_TIMER) {
+			return cd->gate_array[offset];
+		}
+		//TODO: open bus maybe?
+		return 0xFFFF;
+	}
 }
 
 static uint8_t main_gate_read8(uint32_t address, void *vcontext)
 {
-	m68k_context *m68k = vcontext;
-	segacd_context *cd = m68k->system;
-	uint16_t val = cd->gate_array[(address & 0x1FF) >> 1];
+	uint16_t val = main_gate_read16(address & 0xFE, vcontext);
 	return address & 1 ? val : val >> 8;
 }
 
@@ -151,21 +499,80 @@
 	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 0x7:
+	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;
+		}
+		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);
+		}
+		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] &= 0x001D;
+		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 {
+			cd->gate_array[reg] |= value & BIT_DMNA;
+			//2M mode
+			if (changed & value & 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;
+
+				m68k_invalidate_code_range(m68k, cd->base + 0x200000, cd->base + 0x240000);
+				m68k_invalidate_code_range(cd->m68k, 0x080000, 0x0C0000);
+			}
+		}
+		if (changed & MASK_PROG_BANK) {
+			uint32_t bank = cd->gate_array[GA_MEM_MODE] >> 6 & 0x3;
+			m68k->mem_pointers[cd->memptr_start_index] = cd->word_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;
 		break;
-	case 0x8:
-	case 0x9:
-	case 0xA:
-	case 0xB:
-	case 0xC:
-	case 0xD:
-	case 0xE:
-	case 0xF:
+	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
 		cd->gate_array[reg] = value;
 		break;
@@ -193,17 +600,17 @@
 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, 0x00FEFF, 0x0000, .flags=MMAP_READ | MMAP_CODE, .write_16 = prog_ram_wp_write16, .write_8 = prog_ram_wp_write8},
-		{0x00FF00, 0x07FFFF, 0x0000, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE},
-		{0x080000, 0x0BFFFF, 0x0000, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE | MMAP_PTR_IDX | MMAP_FUNC_NULL, .ptr_index = 0,
-			.read_16 = work_ram_2M_read16, .write_16 = work_ram_2M_write16, .read_8 = work_ram_2M_read8, .write_8 = work_ram_2M_write8},
-		{0x0C0000, 0x0DFFFF, 0x0000, .flags=MMAP_READ | MMAP_WRITE | MMAP_CODE | MMAP_PTR_IDX | MMAP_FUNC_NULL, .ptr_index = 1,
-			.read_16 = work_ram_1M_read16, .write_16 = work_ram_1M_write16, .read_8 = work_ram_1M_read8, .write_8 = work_ram_1M_write8},
-		{0xFE0000, 0xFEFFFF, 0x3FFF, .flags=MMAP_READ | MMAP_WRITE | MMAP_ONLY_ODD},
-		{0xFF0000, 0xFF7FFF, 0x0000, .read_16 = pcm_read16, .write_16 = pcm_write16, .read_8 = pcm_read8, .write_8 = pcm_write8},
-		{0xFF8000, 0xFF81FF, 0x0000, .read_16 = sub_gate_read16, .write_16 = sub_gate_write16, .read_8 = sub_gate_read8, .write_8 = sub_gate_write8}
+		{0x000000, 0x00FEFF, 0xFFFFFF, .flags=MMAP_READ | MMAP_CODE, .write_16 = prog_ram_wp_write16, .write_8 = prog_ram_wp_write8},
+		{0x00FF00, 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}
 	};
-	memset(info, 0, sizeof(*info));
+
 	segacd_context *cd = calloc(sizeof(segacd_context), 1);
 	FILE *f = fopen("cdbios.bin", "rb");
 	if (!f) {
@@ -218,46 +625,67 @@
 	cd->rom_mut = malloc(adjusted_size);
 	byteswap_rom(adjusted_size, cd->rom);
 	memcpy(cd->rom_mut, cd->rom, adjusted_size);
-	
-	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->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->work_ram = malloc(256*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);
+	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;
+
 	return cd;
 }
 
-memmap_chunk *segacd_main_cpu_map(segacd_context *cd, uint32_t *num_chunks)
+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, 0xFFFFFF, .flags=MMAP_READ},
-		{0x020000, 0x03FFFF, 0x1FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL, .ptr_index = 0},//TODO: support running main CPU code from here
-		{0x040000, 0x05FFFF, 0x1FFFF, .flags=MMAP_READ}, //first ROM alias
+		{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, 0x01FFFF, 0x1FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL, .ptr_index = 1},
-		{0x220000, 0x03FFFF, 0x1FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL, .ptr_index = 2},
+		{0x200000, 0x21FFFF, 0x01FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_CODE, .ptr_index = 1},
+		{0x220000, 0x23FFFF, 0x01FFFF, .flags=MMAP_READ|MMAP_WRITE|MMAP_PTR_IDX|MMAP_FUNC_NULL|MMAP_CODE, .ptr_index = 2},
 		{0xA12000, 0xA12FFF, 0xFFFFFF, .read_16 = main_gate_read16, .write_16 = main_gate_write16, .read_8 = main_gate_read8, .write_8 = main_gate_write8}
 	};
-	//TODO: support cart boot maps
+	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->work_ram;
-	main_cpu_map[3].buffer = cd->work_ram + 0x10000;
+	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;
 }
--- a/segacd.h	Sat Jan 15 13:15:21 2022 -0800
+++ b/segacd.h	Tue Jan 18 00:03:50 2022 -0800
@@ -1,25 +1,36 @@
 #ifndef SEGACD_H_
 #define SEGACD_H_
 #include <stdint.h>
-#include "system.h"
-#include "m68k_core.h"
+#include "genesis.h"
 
 typedef struct {
-	m68k_context *m68k;
-	system_media *media;
-	uint16_t     gate_array[0x100];
-	uint8_t      busreq;
-	uint8_t      busack;
-	uint8_t      reset;
-	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     *work_ram;
-	uint8_t      *pcm_ram;
-	uint8_t      *bram;
+	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        periph_reset_cycle;
+	uint32_t        base;
+	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;
 } 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, uint32_t *num_chunks);
+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_