changeset 987:1f09994e92c5

Initial stab at implementing address error exceptions. Need to fill in the value of IR, undefined bits of last stack frame word and properly deal with address errors that occur during exception processing.
author Michael Pavone <pavone@retrodev.com>
date Tue, 26 Apr 2016 23:13:37 -0700
parents f680fe746a7d
children ce9df7a5fdf2
files backend.h backend_x86.c m68k_core.c m68k_core.h m68k_core_x86.c m68k_internal.h
diffstat 6 files changed, 181 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/backend.h	Tue Apr 26 00:07:15 2016 -0700
+++ b/backend.h	Tue Apr 26 23:13:37 2016 -0700
@@ -86,6 +86,8 @@
 	code_ptr           handle_cycle_limit;
 	code_ptr           handle_cycle_limit_int;
 	code_ptr           handle_code_write;
+	code_ptr           handle_align_error_write;
+	code_ptr           handle_align_error_read;
 	uint32_t           memmap_chunks;
 	uint32_t           address_mask;
 	uint32_t           max_address;
@@ -101,6 +103,7 @@
 	uint8_t            limit;
 	uint8_t			   scratch1;
 	uint8_t			   scratch2;
+	uint8_t            align_error_mask;
 } cpu_options;
 
 typedef uint8_t * (*native_addr_func)(void * context, uint32_t address);
--- a/backend_x86.c	Tue Apr 26 00:07:15 2016 -0700
+++ b/backend_x86.c	Tue Apr 26 23:13:37 2016 -0700
@@ -51,18 +51,23 @@
 	code_info *code = &opts->code;
 	code_ptr start = code->cur;
 	check_cycles(opts);
+	uint8_t is_write = fun_type == WRITE_16 || fun_type == WRITE_8;
+	uint8_t adr_reg = is_write ? opts->scratch2 : opts->scratch1;
+	uint8_t size =  (fun_type == READ_16 || fun_type == WRITE_16) ? SZ_W : SZ_B;
+	if (size != SZ_B && opts->align_error_mask) {
+		test_ir(code, opts->align_error_mask, adr_reg, SZ_D);
+		jcc(code, CC_NZ, is_write ? opts->handle_align_error_write : opts->handle_align_error_read);
+	}
 	cycles(opts, opts->bus_cycles);
 	if (after_inc) {
 		*after_inc = code->cur;
 	}
-	uint8_t is_write = fun_type == WRITE_16 || fun_type == WRITE_8;
-	uint8_t adr_reg = is_write ? opts->scratch2 : opts->scratch1;
+	
 	if (opts->address_size == SZ_D && opts->address_mask != 0xFFFFFFFF) {
 		and_ir(code, opts->address_mask, adr_reg, SZ_D);
 	}
 	code_ptr lb_jcc = NULL, ub_jcc = NULL;
 	uint16_t access_flag = is_write ? MMAP_WRITE : MMAP_READ;
-	uint8_t size =  (fun_type == READ_16 || fun_type == WRITE_16) ? SZ_W : SZ_B;
 	uint32_t ram_flags_off = opts->ram_flags_off;
 	for (uint32_t chunk = 0; chunk < num_chunks; chunk++)
 	{
--- a/m68k_core.c	Tue Apr 26 00:07:15 2016 -0700
+++ b/m68k_core.c	Tue Apr 26 23:13:37 2016 -0700
@@ -553,9 +553,7 @@
 {
 	native_map_slot * native_code_map = opts->gen.native_code_map;
 	address &= opts->gen.address_mask;
-	if (address & 1) {
-		return opts->odd_address;
-	}
+	
 	//TODO: Refactor part of this loop into some kind of get_ram_chunk function
 	for (int i = 0; i < opts->gen.memmap_chunks; i++) {
 		if (address >= opts->gen.memmap[i].start && address < opts->gen.memmap[i].end) {
@@ -563,7 +561,7 @@
 			address = opts->gen.memmap[i].start + ((address - opts->gen.memmap[i].start) & opts->gen.memmap[i].mask);
 		}
 	}
-	address /= 2;
+	
 	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
 	if (!native_code_map[chunk].base) {
 		return NULL;
@@ -591,7 +589,6 @@
 		}
 	}
 	
-	address /= 2;
 	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
 	if (!native_code_map[chunk].base) {
 		return 0;
@@ -643,7 +640,7 @@
 			meta_off += size;
 		}
 	}
-	address/= 2;
+	
 	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
 	if (!native_code_map[chunk].base) {
 		native_code_map[chunk].base = native_addr;
@@ -819,6 +816,10 @@
 
 void translate_m68k(m68k_options * opts, m68kinst * inst)
 {
+	if (inst->address & 1) {
+		translate_m68k_odd(opts, inst);
+		return;
+	}
 	check_cycles_int(&opts->gen, inst->address);
 	//log_address(&opts->gen, inst->address, "M68K: %X @ %d\n");
 	if (
@@ -872,9 +873,6 @@
 			fflush(opts->address_log);
 		}
 		do {
-			if (address & 1) {
-				break;
-			}
 			encoded = get_native_pointer(address, (void **)context->mem_pointers, &opts->gen);
 			if (!encoded) {
 				map_native_address(context, address, code->cur, 2, 1);
@@ -902,7 +900,7 @@
 			translate_m68k(opts, &instbuf);
 			code_ptr after = code->cur;
 			map_native_address(context, instbuf.address, start, m68k_size, after-start);
-		} while(!m68k_is_terminal(&instbuf));
+		} while(!m68k_is_terminal(&instbuf) && !(address & 1));
 		process_deferred(&opts->gen.deferred, context, (native_addr_func)get_native_from_context);
 		if (opts->gen.deferred) {
 			address = opts->gen.deferred->address;
--- a/m68k_core.h	Tue Apr 26 00:07:15 2016 -0700
+++ b/m68k_core.h	Tue Apr 26 23:13:37 2016 -0700
@@ -13,7 +13,7 @@
 
 #define NUM_MEM_AREAS 8
 #define NATIVE_MAP_CHUNKS (64*1024)
-#define NATIVE_CHUNK_SIZE ((16 * 1024 * 1024 / NATIVE_MAP_CHUNKS)/2)
+#define NATIVE_CHUNK_SIZE ((16 * 1024 * 1024 / NATIVE_MAP_CHUNKS))
 #define MAX_NATIVE_SIZE 255
 
 #define M68K_OPT_BROKEN_READ_MODIFY 1
@@ -36,7 +36,6 @@
 	code_ptr        write_32_highfirst;
 	code_ptr        do_sync;
 	code_ptr        trap;
-	code_ptr        odd_address;
 	start_fun       start_context;
 	code_ptr        retrans_stub;
 	code_ptr        native_addr;
--- a/m68k_core_x86.c	Tue Apr 26 00:07:15 2016 -0700
+++ b/m68k_core_x86.c	Tue Apr 26 23:13:37 2016 -0700
@@ -2128,6 +2128,54 @@
 	*no_trap = code->cur - (no_trap + 1);
 }
 
+void translate_m68k_odd(m68k_options *opts, m68kinst *inst)
+{
+	code_info *code = &opts->gen.code;
+	//swap USP and SSP if not already in supervisor mode
+	check_user_mode_swap_ssp_usp(opts);
+	//save PC
+	subi_areg(opts, 4, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
+	call(code, opts->write_32_lowfirst);
+	//save status register
+	subi_areg(opts, 2, 7);
+	call(code, opts->get_sr);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save instruction register
+	subi_areg(opts, 2, 7);
+	//TODO: Use actual value
+	mov_ir(code, 0, opts->gen.scratch1, SZ_W);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save access address
+	subi_areg(opts, 4, 7);
+	mov_ir(code, inst->address, opts->gen.scratch1, SZ_D);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_32_lowfirst);
+	//save FC, I/N and R/W word'
+	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
+	//FC3 is basically the same as the supervisor bit
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
+	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
+	and_ir(code, 4, opts->gen.scratch1, SZ_B);
+	//set FC1 to one to indicate instruction fetch, and R/W to indicate read
+	or_ir(code, 0x12, opts->gen.scratch1, SZ_B);
+	//TODO: Figure out what undefined bits get set to, looks like it might be value of IR
+	subi_areg(opts, 2, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//set supervisor bit
+	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
+	//load vector address
+	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
+	call(code, opts->read_32);
+	call(code, opts->native_addr_and_sync);
+	cycles(&opts->gen, 18);
+	jmp_r(code, opts->gen.scratch1);
+}
+
 void translate_m68k_move_from_sr(m68k_options *opts, m68kinst *inst, host_ea *src_op, host_ea *dst_op)
 {
 	code_info *code = &opts->gen.code;
@@ -2285,6 +2333,7 @@
 	opts->gen.cycles = RAX;
 	opts->gen.limit = RBP;
 	opts->gen.scratch1 = RCX;
+	opts->gen.align_error_mask = 1;
 
 
 	opts->gen.native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS);
@@ -2392,7 +2441,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);
@@ -2493,6 +2549,109 @@
 		}
 	}
 	retn(code);
+	
+	code_info tmp_code = *code;
+	code->cur = opts->gen.handle_align_error_write;
+	code->last = code->cur + 256;
+	//unwind the stack one functinon call
+	add_ir(code, 16, RSP, SZ_PTR);
+	//save address that triggered error so we can write it to the 68K stack at the appropriate place
+	push_r(code, opts->gen.scratch2);
+	//swap USP and SSP if not already in supervisor mode
+	check_user_mode_swap_ssp_usp(opts);
+	//save PC
+	subi_areg(opts, 4, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
+	call(code, opts->write_32_lowfirst);
+	//save status register
+	subi_areg(opts, 2, 7);
+	call(code, opts->get_sr);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save instruction register
+	subi_areg(opts, 2, 7);
+	//TODO: Use actual value
+	mov_ir(code, 0, opts->gen.scratch1, SZ_W);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save access address
+	subi_areg(opts, 4, 7);
+	pop_r(code, opts->gen.scratch1);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_32_lowfirst);
+	//save FC, I/N and R/W word'
+	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
+	//FC3 is basically the same as the supervisor bit
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
+	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
+	and_ir(code, 4, opts->gen.scratch1, SZ_B);
+	//set FC0 to one to indicate data access
+	or_ir(code, 1, opts->gen.scratch1, SZ_B);
+	//TODO: Figure out what undefined bits get set to, looks like it might be value of IR
+	subi_areg(opts, 2, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//set supervisor bit
+	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
+	//load vector address
+	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
+	call(code, opts->read_32);
+	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
+	add_ir(code, 16, RSP, SZ_PTR);
+	//save address that triggered error so we can write it to the 68K stack at the appropriate place
+	push_r(code, opts->gen.scratch1);
+	//swap USP and SSP if not already in supervisor mode
+	check_user_mode_swap_ssp_usp(opts);
+	//save PC
+	subi_areg(opts, 4, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, last_prefetch_address), opts->gen.scratch1, SZ_D);
+	call(code, opts->write_32_lowfirst);
+	//save status register
+	subi_areg(opts, 2, 7);
+	call(code, opts->get_sr);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save instruction register
+	subi_areg(opts, 2, 7);
+	//TODO: Use actual value
+	mov_ir(code, 0, opts->gen.scratch1, SZ_W);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//save access address
+	subi_areg(opts, 4, 7);
+	pop_r(code, opts->gen.scratch1);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_32_lowfirst);
+	//save FC, I/N and R/W word'
+	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_W);
+	//FC3 is basically the same as the supervisor bit
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, status), opts->gen.scratch1, SZ_B);
+	shr_ir(code, 3, opts->gen.scratch1, SZ_B);
+	and_ir(code, 4, opts->gen.scratch1, SZ_B);
+	//set FC0 to one to indicate data access, and R/W to indicate read
+	or_ir(code, 0x11, opts->gen.scratch1, SZ_B);
+	//TODO: Figure out what undefined bits get set to, looks like it might be value of IR
+	subi_areg(opts, 2, 7);
+	areg_to_native(opts, 7, opts->gen.scratch2);
+	call(code, opts->write_16);
+	//set supervisor bit
+	or_irdisp(code, 0x20, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
+	//load vector address
+	mov_ir(code, 4 * VECTOR_ADDRESS_ERROR, opts->gen.scratch1, SZ_D);
+	call(code, opts->read_32);
+	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;
 	//calculate stack adjust size
@@ -2630,11 +2789,4 @@
 	call(code, opts->native_addr_and_sync);
 	cycles(&opts->gen, 18);
 	jmp_r(code, opts->gen.scratch1);
-
-	opts->odd_address = code->cur;
-	mov_ir(code, (int64_t)stderr, RDI, SZ_PTR);
-	mov_ir(code, (int64_t)"Attempt to execute code at odd address\n", RSI, SZ_PTR);
-	call_args_abi(code, (code_ptr)fprintf, 2, RDI, RSI, RDX);
-	xor_rr(code, RDI, RDI, SZ_D);
-	call_args(code, (code_ptr)exit, 1, RDI);
 }
--- a/m68k_internal.h	Tue Apr 26 00:07:15 2016 -0700
+++ b/m68k_internal.h	Tue Apr 26 23:13:37 2016 -0700
@@ -33,6 +33,7 @@
 void nop_fill_or_jmp_next(code_info *code, code_ptr old_end, code_ptr next_inst);
 void check_user_mode_swap_ssp_usp(m68k_options *opts);
 void m68k_set_last_prefetch(m68k_options *opts, uint32_t address);
+void translate_m68k_odd(m68k_options *opts, m68kinst *inst);
 
 //functions implemented in m68k_core.c
 int8_t native_reg(m68k_op_info * op, m68k_options * opts);