changeset 351:2f264d2a60c2

Support for SRAM with SEGA mapper. Half-finished support for SRAM without SEGA mapper.
author Mike Pavone <pavone@retrodev.com>
date Tue, 21 May 2013 22:08:59 -0700
parents 91aa2aa05e68
children 8c3409585130
files blastem.c blastem.h m68k_to_x86.c x86_backend.h
diffstat 4 files changed, 353 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Tue May 21 19:26:20 2013 -0700
+++ b/blastem.c	Tue May 21 22:08:59 2013 -0700
@@ -812,6 +812,127 @@
 	return ym_read_status(gen->ym);
 }
 
+uint16_t read_sram_w(uint32_t address, m68k_context * context)
+{
+	genesis_context * gen = context->system;
+	address &= gen->save_ram_mask;
+	switch(gen->save_flags)
+	{
+	case RAM_FLAG_BOTH:
+		return gen->save_ram[address] << 8 | gen->save_ram[address+1];
+	case RAM_FLAG_EVEN:
+		return gen->save_ram[address >> 1] << 8 | 0xFF;
+	case RAM_FLAG_ODD:
+		return gen->save_ram[address >> 1] | 0xFF00;
+	}
+	return 0xFFFF;//We should never get here
+}
+
+uint8_t read_sram_b(uint32_t address, m68k_context * context)
+{
+	genesis_context * gen = context->system;
+	address &= gen->save_ram_mask;
+	switch(gen->save_flags)
+	{
+	case RAM_FLAG_BOTH:
+		return gen->save_ram[address];
+	case RAM_FLAG_EVEN:
+		if (address & 1) {
+			return 0xFF;
+		} else {
+			return gen->save_ram[address >> 1];
+		}
+	case RAM_FLAG_ODD:
+		if (address & 1) {
+			return gen->save_ram[address >> 1];
+		} else {
+			return 0xFF;
+		}
+	}
+	return 0xFF;//We should never get here
+}
+
+m68k_context * write_sram_area_w(uint32_t address, m68k_context * context, uint16_t value)
+{
+	genesis_context * gen = context->system;
+	if ((gen->bank_regs[0] & 0x3) == 1) {
+		address &= gen->save_ram_mask;
+		switch(gen->save_flags)
+		{
+		case RAM_FLAG_BOTH:
+			gen->save_ram[address] = value >> 8;
+			gen->save_ram[address+1] = value;
+			break;
+		case RAM_FLAG_EVEN:
+			gen->save_ram[address >> 1] = value >> 8;
+			break;
+		case RAM_FLAG_ODD:
+			gen->save_ram[address >> 1] = value;
+			break;
+		}
+	}
+	return context;
+}
+
+m68k_context * write_sram_area_b(uint32_t address, m68k_context * context, uint8_t value)
+{
+	genesis_context * gen = context->system;
+	if ((gen->bank_regs[0] & 0x3) == 1) {
+		address &= gen->save_ram_mask;
+		switch(gen->save_flags)
+		{
+		case RAM_FLAG_BOTH:
+			gen->save_ram[address] = value;
+			break;
+		case RAM_FLAG_EVEN:
+			if (!(address & 1)) {
+				gen->save_ram[address >> 1] = value;
+			}
+			break;
+		case RAM_FLAG_ODD:
+			if (address & 1) {
+				gen->save_ram[address >> 1] = value;
+			}
+			break;
+		}
+	}
+	return context;
+}
+
+m68k_context * write_bank_reg_w(uint32_t address, m68k_context * context, uint16_t value)
+{
+	genesis_context * gen = context->system;
+	address &= 0xE;
+	address >>= 1;
+	gen->bank_regs[address] = value;
+	if (!address) {
+		if (value & 1) {
+			context->mem_pointers[2] = NULL;
+		} else {
+			context->mem_pointers[2] = cart + 0x200000/2;
+		}
+	}
+	return context;
+}
+
+m68k_context * write_bank_reg_b(uint32_t address, m68k_context * context, uint8_t value)
+{
+	if (address & 1) {
+		genesis_context * gen = context->system;
+		address &= 0xE;
+		address >>= 1;
+		gen->bank_regs[address] = value;
+		if (!address) {
+			if (value & 1) {
+				context->mem_pointers[2] = NULL;
+			} else {
+				context->mem_pointers[2] = cart + 0x200000/2;
+			}
+		}
+	}
+	return context;
+}
+
 typedef struct bp_def {
 	struct bp_def * next;
 	uint32_t address;
@@ -1054,13 +1175,16 @@
 	return context;
 }
 
-void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
-{
-	m68k_context context;
-	x86_68k_options opts;
-	gen->m68k = &context;
-	memmap_chunk memmap[] = {
-		{0,        0x400000,  0xFFFFFF, 0, MMAP_READ | MMAP_WRITE,             cart,
+#define ROM_END   0x1A4
+#define RAM_ID    0x1B0
+#define RAM_FLAGS 0x1B2
+#define RAM_START 0x1B4
+#define RAM_END   0x1B8
+#define MAX_MAP_CHUNKS (4+7+1)
+#define RAM_FLAG_MASK 0x1800
+
+const memmap_chunk static_map[] = {
+		{0,        0x400000,  0xFFFFFF, 0, MMAP_READ,                          cart,
 		           NULL,          NULL,         NULL,            NULL},
 		{0xE00000, 0x1000000, 0xFFFF,   0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, 
 		           NULL,          NULL,         NULL,            NULL},
@@ -1071,7 +1195,123 @@
 		           (read_16_fun)io_read_w,      (write_16_fun)io_write_w,
 		           (read_8_fun)io_read,         (write_8_fun)io_write}
 	};
-	init_x86_68k_opts(&opts, memmap, sizeof(memmap)/sizeof(memmap_chunk));
+
+char * sram_filename;
+genesis_context * genesis;
+void save_sram()
+{
+	FILE * f = fopen(sram_filename, "wb");
+	if (!f) {
+		fprintf(stderr, "Failed to open SRAM file %s for writing\n", sram_filename);
+		return;
+	}
+	uint32_t size = genesis->save_ram_mask+1;
+	if (genesis->save_flags != RAM_FLAG_BOTH) {
+		size/= 2;
+	}
+	fwrite(genesis->save_ram, 1, size, f);
+	fclose(f);
+	printf("Saved SRAM to %s\n", sram_filename);
+}
+
+void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
+{
+	m68k_context context;
+	x86_68k_options opts;
+	gen->m68k = &context;
+	memmap_chunk memmap[MAX_MAP_CHUNKS];
+	uint32_t num_chunks;
+	void * initial_mapped = NULL;
+	gen->save_ram = NULL;
+	//TODO: Handle carts larger than 4MB
+	//TODO: Handle non-standard mappers
+	uint32_t size;
+	if ((cart[RAM_ID/2] & 0xFF) == 'A' && (cart[RAM_ID/2] >> 8) == 'R') {
+		//Cart has save RAM
+		uint32_t rom_end = ((cart[ROM_END/2] << 16) | cart[ROM_END/2+1]) + 1;
+		uint32_t ram_start = (cart[RAM_START/2] << 16) | cart[RAM_START/2+1];
+		uint32_t ram_end = (cart[RAM_END/2] << 16) | cart[RAM_END/2+1];
+		uint16_t ram_flags = cart[RAM_FLAGS/2];
+		gen->save_flags = ram_flags & RAM_FLAG_MASK;
+		memset(memmap, 0, sizeof(memmap_chunk)*2);
+		if (ram_start >= rom_end) {
+			memmap[0].end = rom_end;
+			memmap[0].mask = 0xFFFFFF;
+			memmap[0].flags = MMAP_READ;
+			memmap[0].buffer = cart;
+			
+			ram_start &= 0xFFFFFE;
+			ram_end |= 1;
+			memmap[1].start = ram_start;
+			gen->save_ram_mask = memmap[1].mask = ram_end-ram_start;
+			ram_end += 1;
+			memmap[1].end = ram_end;
+			memmap[1].flags = MMAP_READ | MMAP_WRITE;
+			size = ram_end-ram_start;
+			if ((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_ODD) {
+				memmap[1].flags |= MMAP_ONLY_ODD;
+				size /= 2;
+			} else if((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_EVEN) {
+				memmap[1].flags |= MMAP_ONLY_EVEN;
+				size /= 2;
+			}
+			memmap[1].buffer = gen->save_ram = malloc(size);
+			
+			memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0]));
+			num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1;
+		} else {
+			//Assume the standard Sega mapper for now
+			memmap[0].end = 0x200000;
+			memmap[0].mask = 0xFFFFFF;
+			memmap[0].flags = MMAP_READ;
+			memmap[0].buffer = cart;
+			
+			memmap[1].start = 0x200000;
+			memmap[1].end = 0x400000;
+			memmap[1].mask = 0x1FFFFF;
+			ram_start &= 0xFFFFFE;
+			ram_end |= 1;
+			gen->save_ram_mask = ram_end-ram_start;
+			memmap[1].flags = MMAP_READ | MMAP_PTR_IDX | MMAP_FUNC_NULL;
+			memmap[1].ptr_index = 2;
+			memmap[1].read_16 = (read_16_fun)read_sram_w;//these will only be called when mem_pointers[2] == NULL
+			memmap[1].read_8 = (read_8_fun)read_sram_b;
+			memmap[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area
+			memmap[1].write_8 = (write_8_fun)write_sram_area_b;
+			memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0]));
+			num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1;
+			memset(memmap+num_chunks, 0, sizeof(memmap[num_chunks]));
+			memmap[num_chunks].start = 0xA13000;
+			memmap[num_chunks].end = 0xA13100;
+			memmap[num_chunks].mask = 0xFF;
+			memmap[num_chunks].write_16 = (write_16_fun)write_bank_reg_w;
+			memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; 
+			num_chunks++;
+			ram_end++;
+			size = ram_end-ram_start;
+			if ((ram_flags & RAM_FLAG_MASK) != RAM_FLAG_BOTH) {
+				size /= 2;
+			}
+			gen->save_ram = malloc(size);
+			memmap[1].buffer = initial_mapped = cart + 0x200000/2;
+		}
+	} else {
+		memcpy(memmap, static_map, sizeof(static_map));
+		num_chunks = sizeof(static_map)/sizeof(memmap_chunk);
+	}
+	if (gen->save_ram) {
+		memset(gen->save_ram, 0, size);
+		FILE * f = fopen(sram_filename, "rb");
+		if (f) {
+			uint32_t read = fread(gen->save_ram, 1, size, f);
+			fclose(f);
+			if (read > 0) {
+				printf("Loaded SRAM from %s\n", sram_filename);
+			}
+		}
+		atexit(save_sram);
+	}
+	init_x86_68k_opts(&opts, memmap, num_chunks);
 	opts.address_log = address_log;
 	init_68k_context(&context, opts.native_code_map, &opts);
 	
@@ -1082,13 +1322,10 @@
 	context.target_cycle = context.sync_cycle = mclks_per_frame/MCLKS_PER_68K;
 	//work RAM
 	context.mem_pointers[1] = ram;
+	//save RAM/map
+	context.mem_pointers[2] = initial_mapped;
+	context.mem_pointers[3] = (uint16_t *)gen->save_ram;
 	uint32_t address;
-	/*address = cart[0x68/2] << 16 | cart[0x6A/2];
-	translate_m68k_stream(address, &context);
-	address = cart[0x70/2] << 16 | cart[0x72/2];
-	translate_m68k_stream(address, &context);
-	address = cart[0x78/2] << 16 | cart[0x7A/2];
-	translate_m68k_stream(address, &context);*/
 	address = cart[2] << 16 | cart[3];
 	translate_m68k_stream(address, &context);
 	if (debug) {
@@ -1249,6 +1486,21 @@
 	gen.z80 = &z_context;
 	gen.vdp = &v_context;
 	gen.ym = &y_context;
+	genesis = &gen;
+	
+	int fname_size = strlen(argv[1]);
+	sram_filename = malloc(fname_size+6);
+	memcpy(sram_filename, argv[1], fname_size);
+	int i;
+	for (i = fname_size-1; fname_size >= 0; --i) {
+		if (sram_filename[i] == '.') {
+			strcpy(sram_filename + i + 1, "sram");
+			break;
+		}
+	}
+	if (i < 0) {
+		strcpy(sram_filename + fname_size, ".sram");
+	}
 	
 	init_run_cpu(&gen, debug, address_log);
 	return 0;
--- a/blastem.h	Tue May 21 19:26:20 2013 -0700
+++ b/blastem.h	Tue May 21 22:08:59 2013 -0700
@@ -15,11 +15,19 @@
 	uint8_t input[3];
 } io_port;
 
+#define RAM_FLAG_ODD  0x1800
+#define RAM_FLAG_EVEN 0x1000
+#define RAM_FLAG_BOTH 0x0000
+
 typedef struct {
 	m68k_context   *m68k;
 	z80_context    *z80;
 	vdp_context    *vdp;
 	ym2612_context *ym;
+	uint8_t        *save_ram;
+	uint32_t       save_ram_mask;
+	uint32_t       save_flags;
+	uint8_t        bank_regs[8];
 } genesis_context;
 
 #define GAMEPAD_TH0 0
--- a/m68k_to_x86.c	Tue May 21 19:26:20 2013 -0700
+++ b/m68k_to_x86.c	Tue May 21 22:08:59 2013 -0700
@@ -4192,7 +4192,78 @@
 		default:
 			cfun = NULL;
 		}
-		if (cfun) {
+		if(memmap[chunk].buffer && memmap[chunk].flags & access_flag) {
+			if (memmap[chunk].flags & MMAP_PTR_IDX) {
+				if (memmap[chunk].flags & MMAP_FUNC_NULL) {
+					dst = cmp_irdisp8(dst, 0, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, SZ_Q);
+					uint8_t * not_null = dst+1;
+					dst = jcc(dst, CC_NZ, dst+2);
+					dst = call(dst, (uint8_t *)m68k_save_context);
+					if (is_write) {
+						//SCRATCH2 is RDI, so no need to move it there
+						dst = mov_rr(dst, SCRATCH1, RDX, size);
+					} else {
+						dst = push_r(dst, CONTEXT);
+						dst = mov_rr(dst, SCRATCH1, RDI, SZ_D);
+					}
+					dst = call(dst, cfun);
+					if (is_write) {
+						dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+					} else {
+						dst = pop_r(dst, CONTEXT);
+						dst = mov_rr(dst, RAX, SCRATCH1, size);
+					}
+					dst = jmp(dst, (uint8_t *)m68k_load_context);
+					
+					*not_null = dst - (not_null + 1);
+				}
+				if (size == SZ_B) {
+					dst = xor_ir(dst, 1, adr_reg, SZ_D);
+				}
+				dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, adr_reg, SZ_Q);
+				if (is_write) {
+					dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size);
+					
+				} else {
+					dst = mov_rindr(dst, SCRATCH1, SCRATCH1, size);
+				}
+			} else {
+				if (size == SZ_B) {
+					dst = xor_ir(dst, 1, adr_reg, SZ_D);
+				}
+				if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
+					if (is_write) {
+						dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (int64_t)memmap[chunk].buffer, size);
+					} else {
+						dst = mov_rdisp32r(dst, SCRATCH1, (int64_t)memmap[chunk].buffer, SCRATCH1, size);
+					}
+				} else {
+					if (is_write) {
+						dst = push_r(dst, SCRATCH1);
+						dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_Q);
+						dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_Q);
+						dst = pop_r(dst, SCRATCH1);
+						dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size);
+					} else {
+						dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH2, SZ_Q);
+						dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 1, SCRATCH1, size);
+					}
+				}
+			}
+			if (is_write && (memmap[chunk].flags & MMAP_CODE)) {
+				dst = mov_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
+				dst = shr_ir(dst, 11, SCRATCH1, SZ_D);
+				dst = bt_rrdisp32(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, ram_code_flags), SZ_D);
+				uint8_t * not_code = dst+1;
+				dst = jcc(dst, CC_NC, dst+2);
+				dst = call(dst, (uint8_t *)m68k_save_context);
+				dst = call(dst, (uint8_t *)m68k_handle_code_write);
+				dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+				dst = call(dst, (uint8_t *)m68k_load_context);
+				*not_code = dst - (not_code+1);
+			}
+			dst = retn(dst);
+		} else if (cfun) {
 			dst = call(dst, (uint8_t *)m68k_save_context);
 			if (is_write) {
 				//SCRATCH2 is RDI, so no need to move it there
@@ -4209,41 +4280,6 @@
 				dst = mov_rr(dst, RAX, SCRATCH1, size);
 			}
 			dst = jmp(dst, (uint8_t *)m68k_load_context);
-		} else if(memmap[chunk].buffer && memmap[chunk].flags & access_flag) {
-			if (size == SZ_B) {
-				dst = xor_ir(dst, 1, adr_reg, SZ_D);
-			}
-			if ((int64_t)memmap[chunk].buffer <= 0x7FFFFFFF && (int64_t)memmap[chunk].buffer >= -2147483648) {
-				if (is_write) {
-					dst = mov_rrdisp32(dst, SCRATCH1, SCRATCH2, (int64_t)memmap[chunk].buffer, size);
-				} else {
-					dst = mov_rdisp32r(dst, SCRATCH1, (int64_t)memmap[chunk].buffer, SCRATCH1, size);
-				}
-			} else {
-				if (is_write) {
-					dst = push_r(dst, SCRATCH1);
-					dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH1, SZ_Q);
-					dst = add_rr(dst, SCRATCH1, SCRATCH2, SZ_Q);
-					dst = pop_r(dst, SCRATCH1);
-					dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size);
-				} else {
-					dst = mov_ir(dst, (int64_t)memmap[chunk].buffer, SCRATCH2, SZ_Q);
-					dst = mov_rindexr(dst, SCRATCH2, SCRATCH1, 1, SCRATCH1, size);
-				}
-			}
-			if (is_write && (memmap[chunk].flags & MMAP_CODE)) {
-				dst = mov_rr(dst, SCRATCH2, SCRATCH1, SZ_D);
-				dst = shr_ir(dst, 11, SCRATCH1, SZ_D);
-				dst = bt_rrdisp32(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, ram_code_flags), SZ_D);
-				uint8_t * not_code = dst+1;
-				dst = jcc(dst, CC_NC, dst+2);
-				dst = call(dst, (uint8_t *)m68k_save_context);
-				dst = call(dst, (uint8_t *)m68k_handle_code_write);
-				dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
-				dst = call(dst, (uint8_t *)m68k_load_context);
-				*not_code = dst - (not_code+1);
-			}
-			dst = retn(dst);
 		} else {
 			//Not sure the best course of action here
 			if (!is_write) {
--- a/x86_backend.h	Tue May 21 19:26:20 2013 -0700
+++ b/x86_backend.h	Tue May 21 22:08:59 2013 -0700
@@ -26,10 +26,13 @@
 } deferred_addr;
 
 
-#define MMAP_READ    0x1
-#define MMAP_WRITE   0x2
-#define MMAP_CODE    0x4
-#define MMAP_PTR_IDX 0x8
+#define MMAP_READ      0x01
+#define MMAP_WRITE     0x02
+#define MMAP_CODE      0x04
+#define MMAP_PTR_IDX   0x08
+#define MMAP_ONLY_ODD  0x10
+#define MMAP_ONLY_EVEN 0x20
+#define MMAP_FUNC_NULL 0x40
 
 typedef uint16_t (*read_16_fun)(uint32_t address, void * context);
 typedef uint8_t (*read_8_fun)(uint32_t address, void * context);