changeset 1115:c1e78a101912

WIP Jaguar GPU/DSP emulation
author Michael Pavone <pavone@retrodev.com>
date Mon, 19 Dec 2016 14:16:59 -0800
parents b45d1f64060e
children fe8c79f82c22
files jagcpu.c jagcpu_x86.c
diffstat 2 files changed, 213 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/jagcpu.c	Mon Dec 19 14:16:43 2016 -0800
+++ b/jagcpu.c	Mon Dec 19 14:16:59 2016 -0800
@@ -159,6 +159,24 @@
 		|| (is_gpu && opcode == GPU_STOREP);
 }
 
+uint8_t is_single_source(uint16_t opcode, uint8_t is_gpu)
+{
+	return opcode == JAG_NEG
+		|| opcode == JAG_NOT
+		|| opcode == JAG_ABS
+		|| opcode == GPU_SAT16 //no is_gpu check needed as DSP_SAT16S is also single source
+		|| (is_gpu && (
+			opcode == GPU_SAT8
+			|| opcode == GPU_SAT24
+			|| opcode == GPU_PACK
+			|| opcode == GPU_UNPACK
+		))
+		|| (!is_gpu && (
+			opcode == DSP_SAT32S
+			|| opcode == DSP_MIRROR
+		));
+}
+
 char * jag_cc_names[] = {
 	"t",
 	"ne",
@@ -198,6 +216,11 @@
 	return jag_cc_names[ccnum];
 }
 
+uint8_t jag_is_alwyas_falsse(uint16_t cond)
+{
+	return (cond & 3) == 3 || (cond && 0xC) == 0xC;
+}
+
 uint32_t jag_jr_dest(uint16_t inst, uint32_t address)
 {
 	uint32_t rel = jag_reg1(inst);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jagcpu_x86.c	Mon Dec 19 14:16:59 2016 -0800
@@ -0,0 +1,190 @@
+#include <stdint.h>
+#include "backend.h"
+#include "jagcpu.h"
+#include "gen_x86.h"
+
+void jag_check_resultwrite(jag_cpu_options *opts, uint8_t src, uint8_t dst)
+{
+	code_info *code = &opts->gen.code;
+	cmp_ir(code, JAGCPU_NOREG, opts->resultreg, SZ_B);
+	code_ptr no_result = code->cur + 1;
+	jcc(code, CC_Z, no_result + 1);
+	//save result to current bank
+	movzx_rr(code, opts->resultreg, opts->scratch1, SZ_B, SZ_D);
+	mov_rrindex(code, opts->result, opts->bankptr, opts->scratch1, 4, SZ_D);
+	
+	code_ptr writeback_penatly;
+	if (dst == src) {
+		//unclear if src and dst read can share a port if it's the same register
+		//assume that is the case for now
+		writeback_penalty = NULL;
+	} else {
+		cmp_ir(code, JAGCPU_NOREG, opts->writeback, SZ_B);
+		code_ptr no_writeback_penalty = code->cur + 1;
+		jcc(code, CC_Z, no_writeback_penalty + 1);
+		cmp_ir(code, src, opts->writeback, SZ_B);
+		code_ptr no_writeback_penalty2 = code->cur + 1;
+		jcc(code, CC_Z, no_writeback_penalty2 + 1);
+		cmp_ir(code, dst, opts->writeback, SZ_B);
+		writeback_penalty = code->cur + 1;
+		jcc(code, CC_NZ, writeback_penalty + 1);
+		*no_writeback_penalty = code->cur - (no_writeback_penalty + 1);
+		*no_writeback_penalty2 = code->cur - (no_writeback_penalty2 + 1);
+	}
+	cmp_ir(code, src, opts->resultreg, SZ_B);
+	code_ptr no_result_penalty;
+	if (dst == src) {
+		no_result_penalty = code->cur+1;
+		jcc(code, CC_NZ, no_result_penalty+1);
+	} else {
+		code_ptr result_penalty = code->cur + 1;
+		jcc(code, CC_Z, result_penalty+1);
+		cmp_ir(code, dst, opts->resultreg, SZ_B);
+		no_result_penalty = code->cur+1;
+		jcc(code, CC_NZ, no_result_penalty+1);
+		*result_penalty = code->cur - (result_penalty + 1);
+	}
+	code_ptr penalty = code->cur;
+	cycles(code, 1);
+	*no_result_penalty = code->cur - (no_result_penalty + 1);
+	code_ptr end = code->cur + 1;
+	jmp(end + 1);
+	//No result to save, but there could still be a writeback, source read conflict
+	code_ptr end2 = NULL;
+	
+	cmp_ir(code, JAGCPU_NOREG, opts->writeback, SZ_B);
+	code_ptr no_resultreg_move = code->cur + 1;
+	jcc(code, CC_Z, no_resultreg_move+1);
+	
+	if (src != dst) {
+		//unclear if src and dst read can share a port if it's the same register
+		//assume that is the case for now
+		cmp_ir(code, src, opts->writeback, SZ_B);
+		end2 = code->cur + 1;
+		jcc(code, CC_Z, end2+1);
+		cmp_ir(code, src, opts->writeback, SZ_B);
+		jcc(code, CC_NZ, penalty)
+	}
+	*end = code->cur - (end + 1);
+	if (end2) {
+		*end2 = code->cur - (end2 + 1);
+	}
+	*no_result = code->cur - (no_result + 1);
+	mov_rr(code, opts->resultreg, opts->writeback, SZ_B);
+	*no_resultreg_move = code->cur - (no_resultreg_move + 1);
+}
+
+void jag_check_resultwrite_noread(jag_cpu_options *opts)
+{
+	code_info *code = &opts->gen.code;
+	cmp_ir(code, JAGCPU_NOREG, opts->resultreg, SZ_B);
+	code_ptr no_result = code->cur + 1;
+	jcc(code, CC_Z, no_result + 1);
+	//save result to current bank
+	movzx_rr(code, opts->resultreg, opts->scratch1, SZ_B, SZ_D);
+	mov_rrindex(code, opts->result, opts->bankptr, opts->scratch1, 4, SZ_D);
+	*no_result = code->cur - (no_result + 1);
+	mov_rr(code, opts->resultreg, opts->writeback, SZ_B);
+}
+
+void jag_check_resultwrite_singleread(jag_cpu options *opts, uint8_t reg)
+{
+	code_info *code = &opts->gen.code;
+	cmp_ir(code, JAGCPU_NOREG, opts->resultreg, SZ_B);
+	code_ptr no_result = code->cur + 1;
+	jcc(code, CC_Z, no_result + 1);
+	//save result to current bank
+	movzx_rr(code, opts->resultreg, opts->scratch1, SZ_B, SZ_D);
+	mov_rrindex(code, opts->result, opts->bankptr, opts->scratch1, 4, SZ_D);
+	
+	//check for a scoreboard delay
+	cmp_ir(code, reg, opts->resultreg, SZ_B);
+	code_ptr no_delay = code-.cur + 1;
+	jcc(code, CC_NZ, no_delay + 1);
+	ccylces(code, 1);
+	*no_delay = code->cur - (no_delay = 1);
+	*no_result = code->cur - (no_result + 1);
+	mov_rr(code, opts->resultreg, opts->writeback, SZ_B);
+}
+
+void translate_jag_quickimmed(jag_cpu_options *opts, uint32_t address, uint16_t opcode, uint32_t value, uint16_t dest)
+{
+	jag_check_resultwrite_singleread(opts, dest);
+	switch (opcode)
+	{
+	case ADDQ:
+		break;
+	case ADDQT:
+		break;
+	}
+}
+
+
+uint16_t *translate_jag_inst(uint16_t *stream, jag_cpu_options *opts, uint32_t address)
+{
+	uint16_t inst = *stream
+	++stream;
+	uint16_t opcode = jag_opcode(inst, opts->is_gpu);
+	check_cycles_int(&opts->gen, address);
+	code_info *code = &opts->gen.code;
+	switch (opcode)
+	{
+	case JAG_MOVEI: {
+		uint32_t value = *stream;
+		++stream;
+		value |= *stream << 16;
+		++stream;
+		
+		jag_check_resultwrite_noread(opts);
+		mov_ir(code, value, opts->result, SZ_D);
+		mov_ir(code, jag_reg2(inst), opts->resultreg, SZ_B);
+		break;
+		}
+	case JAG_MOVE: {
+		uint8_t src = jag_reg1(inst), dst = jag_reg2(inst);
+		jag_check_resultwrite_singleread(opts, src);
+		//move has a shorter pipeline than normal instructions
+		if (src != dst) {
+			mov_rdispr(code, opts->bankptr, src * 4, opts->gen.scratch1, SZ_D);
+			mov_rrdisp(code, opts->gen.scratch1, opts->bankptr, dst * 4, SZ_D);
+		}
+		mov_ir(code, dst, opts->writeback, SZ_B);
+		mov_ir(code, JAGCPU_NOREG, opts->resultreg, SZ_B);
+		break;
+		}
+	case JAG_MOVEQ: {
+		uint8_t dst = jag_reg2(inst);
+		jag_check_resultwrite_noread(opts);
+		//moveq has a shorter pipeline than normal instructions
+		mov_irdisp(code, jag_quick(inst), opts->bankptr, dst * 4, SZ_D);
+		mov_ir(code, dst, opts->writeback, SZ_B);
+		mov_ir(code, JAGCPU_NOREG, opts->resultreg, SZ_B);
+		}
+		break;
+	case JAG_JR: {
+		jag_check_resultwrite_noread(opts);
+		//TODO: Pipeline stalls on flag readiness
+		uint16_t cond = jag_reg2(inst);
+		if (jag_is_always_false(cond)) {
+		} else {
+			int32_t offset = jag_quick(inst);
+			if (offset & 0x10) {
+				offset = -16 + (offset & 0xF);
+			}
+		}
+		
+		}
+		break;
+	default:
+		if (is_quick_1_32_opcode(opcode, opts->is_gpu)) {
+			translate_jag_quickimmed(opts, address, jag_quick(inst), jag_reg2(inst));
+		} else if (is_quick_0_31_opcode(opcode)) {
+			translate_jag_quickimmed(opts, address, jag_reg1(inst), jag_reg2(inst));
+		} else if (is_single_source(opcode, opts->is_gpu)) {
+			translate_jag_single_source(opts, address, jag_reg2(isnt));
+		} else {
+			translate_jag_normal(opts, address, jag_reg1(inst), jag_reg2(inst));
+		}
+	}
+	return stream;
+}
\ No newline at end of file