changeset 292:b970ea214ecb

Added z80 test generator and z80 test runner.
author Mike Pavone <pavone@retrodev.com>
date Wed, 08 May 2013 14:40:48 -0700
parents eea3b118940d
children ba97772b1662
files Makefile z80_to_x86.c z80inst.c z80inst.h ztestgen.c ztestrun.c
diffstat 6 files changed, 506 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon May 06 00:57:56 2013 -0700
+++ b/Makefile	Wed May 08 14:40:48 2013 -0700
@@ -25,6 +25,12 @@
 transz80 : transz80.o z80inst.o gen_x86.o z80_to_x86.o x86_backend.o zruntime.o mem.o
 	$(CC) -o transz80 transz80.o z80inst.o gen_x86.o z80_to_x86.o x86_backend.o zruntime.o mem.o
 
+ztestrun : ztestrun.o z80inst.o gen_x86.o z80_to_x86.o x86_backend.o zruntime.o mem.o
+	$(CC) -o ztestrun ztestrun.o z80inst.o gen_x86.o z80_to_x86.o x86_backend.o zruntime.o mem.o
+
+ztestgen : ztestgen.o z80inst.o
+	$(CC) -o ztestgen ztestgen.o z80inst.o
+
 stateview : stateview.o vdp.o render_sdl.o
 	$(CC) -o stateview stateview.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)`
 
--- a/z80_to_x86.c	Mon May 06 00:57:56 2013 -0700
+++ b/z80_to_x86.c	Wed May 08 14:40:48 2013 -0700
@@ -47,54 +47,6 @@
 	return SZ_B;
 }
 
-uint8_t z80_high_reg(uint8_t reg)
-{
-	switch(reg)
-	{
-	case Z80_C:
-	case Z80_BC:
-		return Z80_B;
-	case Z80_E:
-	case Z80_DE:
-		return Z80_D;
-	case Z80_L:
-	case Z80_HL:
-		return Z80_H;
-	case Z80_IXL:
-	case Z80_IX:
-		return Z80_IXH;
-	case Z80_IYL:
-	case Z80_IY:
-		return Z80_IYH;
-	default:
-		return Z80_UNUSED;
-	}
-}
-
-uint8_t z80_low_reg(uint8_t reg)
-{
-	switch(reg)
-	{
-	case Z80_B:
-	case Z80_BC:
-		return Z80_C;
-	case Z80_D:
-	case Z80_DE:
-		return Z80_E;
-	case Z80_H:
-	case Z80_HL:
-		return Z80_L;
-	case Z80_IXH:
-	case Z80_IX:
-		return Z80_IXL;
-	case Z80_IYH:
-	case Z80_IY:
-		return Z80_IYL;
-	default:
-		return Z80_UNUSED;
-	}
-}
-
 uint8_t * zcycles(uint8_t * dst, uint32_t num_cycles)
 {
 	return add_ir(dst, num_cycles, ZCYCLES, SZ_D);
--- a/z80inst.c	Mon May 06 00:57:56 2013 -0700
+++ b/z80inst.c	Wed May 08 14:40:48 2013 -0700
@@ -1459,3 +1459,76 @@
 	return len;
 }
 
+uint8_t z80_high_reg(uint8_t reg)
+{
+	switch(reg)
+	{
+	case Z80_C:
+	case Z80_BC:
+		return Z80_B;
+	case Z80_E:
+	case Z80_DE:
+		return Z80_D;
+	case Z80_L:
+	case Z80_HL:
+		return Z80_H;
+	case Z80_IXL:
+	case Z80_IX:
+		return Z80_IXH;
+	case Z80_IYL:
+	case Z80_IY:
+		return Z80_IYH;
+	default:
+		return Z80_UNUSED;
+	}
+}
+
+uint8_t z80_low_reg(uint8_t reg)
+{
+	switch(reg)
+	{
+	case Z80_B:
+	case Z80_BC:
+		return Z80_C;
+	case Z80_D:
+	case Z80_DE:
+		return Z80_E;
+	case Z80_H:
+	case Z80_HL:
+		return Z80_L;
+	case Z80_IXH:
+	case Z80_IX:
+		return Z80_IXL;
+	case Z80_IYH:
+	case Z80_IY:
+		return Z80_IYL;
+	default:
+		return Z80_UNUSED;
+	}
+}
+
+uint8_t z80_word_reg(uint8_t reg)
+{
+	switch(reg)
+	{
+	case Z80_B:
+	case Z80_C:
+		return Z80_BC;
+	case Z80_D:
+	case Z80_E:
+		return Z80_DE;
+	case Z80_H:
+	case Z80_L:
+		return Z80_HL;
+	case Z80_IXH:
+	case Z80_IXL:
+		return Z80_IX;
+	case Z80_IYH:
+	case Z80_IYL:
+		return Z80_IY;
+	default:
+		return Z80_UNUSED;
+	}
+}
+
+
--- a/z80inst.h	Mon May 06 00:57:56 2013 -0700
+++ b/z80inst.h	Wed May 08 14:40:48 2013 -0700
@@ -132,6 +132,9 @@
 
 uint8_t * z80_decode(uint8_t * istream, z80inst * decoded);
 int z80_disasm(z80inst * decoded, char * dst);
+uint8_t z80_high_reg(uint8_t reg);
+uint8_t z80_low_reg(uint8_t reg);
+uint8_t z80_word_reg(uint8_t reg);
 
 #endif //Z80INST_H_
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ztestgen.c	Wed May 08 14:40:48 2013 -0700
@@ -0,0 +1,332 @@
+#include "z80inst.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+extern z80inst z80_tbl_a[256];
+extern z80inst z80_tbl_extd[0xC0-0x40];
+extern z80inst z80_tbl_bit[256];
+extern z80inst z80_tbl_ix[256];
+extern z80inst z80_tbl_iy[256];
+extern z80inst z80_tbl_ix_bit[256];
+extern z80inst z80_tbl_iy_bit[256];
+extern char *z80_mnemonics[Z80_OTDR+1];
+extern char * z80_regs[Z80_USE_IMMED];
+#define PRE_IX  0xDD
+#define PRE_IY  0xFD
+#define LD_IR16 0x01
+#define LD_IR8  0x06
+#define PUSH    0xC5
+#define POP     0xC1
+
+uint8_t * ld_ir16(uint8_t * dst, uint8_t reg, uint16_t val)
+{
+	if (reg == Z80_IX) {
+		*(dst++) = PRE_IX;
+		return ld_ir16(dst, Z80_HL, val);
+	} else if(reg == Z80_IY) {
+		*(dst++) = PRE_IY;
+		return ld_ir16(dst, Z80_HL, val);
+	} else {
+		*(dst++) = LD_IR16 | ((reg - Z80_BC) << 4);
+		*(dst++) = val & 0xFF;
+		*(dst++) = val >> 8;
+		return dst;
+	}
+}
+
+uint8_t * ld_ir8(uint8_t * dst, uint8_t reg, uint8_t val)
+{
+	if (reg <= Z80_H) {
+		reg = (reg - Z80_C) ^ 1;
+	} else {
+		reg = 0x7;
+	}
+	*(dst++) = LD_IR8 | (reg << 3);
+	*(dst++) = val;
+	return dst;
+}
+
+uint8_t * ld_amem(uint8_t * dst, uint16_t address)
+{
+	*(dst++) = 0x32;
+	*(dst++) = address & 0xFF;
+	*(dst++) = address >> 8;
+	return dst;
+}
+
+uint8_t * push(uint8_t * dst, uint8_t reg)
+{
+	if (reg == Z80_IX) {
+		*(dst++) = PRE_IX;
+		return push(dst, Z80_HL);
+	} else if(reg == Z80_IY) {
+		*(dst++) = PRE_IY;
+		return push(dst, Z80_HL);
+	} else {
+		if (reg == Z80_AF) {
+			reg--;
+		}
+		*(dst++) = PUSH | ((reg - Z80_BC) << 4);
+		return dst;
+	}
+}
+
+uint8_t * pop(uint8_t * dst, uint8_t reg)
+{
+	if (reg == Z80_IX) {
+		*(dst++) = PRE_IX;
+		return pop(dst, Z80_HL);
+	} else if(reg == Z80_IY) {
+		*(dst++) = PRE_IY;
+		return pop(dst, Z80_HL);
+	} else {
+		if (reg == Z80_AF) {
+			reg--;
+		}
+		*(dst++) = POP | ((reg - Z80_BC) << 4);
+		return dst;
+	}
+}
+
+void z80_gen_test(z80inst * inst, uint8_t *instbuf, uint8_t instlen)
+{
+	z80inst copy;
+	uint16_t reg_values[Z80_UNUSED];
+	memset(reg_values, 0, sizeof(reg_values));
+	uint8_t addr_mode = inst->addr_mode & 0x1F;
+	uint8_t word_sized = ((inst->reg != Z80_USE_IMMED && inst->reg != Z80_UNUSED && inst->reg >= Z80_BC) || (addr_mode == Z80_REG && inst->ea_reg >= Z80_BC)) ? 1 : 0;
+
+	if (inst->reg == Z80_USE_IMMED || addr_mode == Z80_IMMED || addr_mode == Z80_IMMED_INDIRECT
+		|| addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE)
+	{
+		memcpy(&copy, inst, sizeof(copy));
+		inst = &copy;
+		if ((inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) 
+			|| (addr_mode == Z80_IMMED && inst->op != Z80_IM))
+		{
+			copy.immed = rand() % (word_sized ? 65536 : 256);
+		}
+		if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) {
+			copy.ea_reg = rand() % 256;
+		}
+		if (addr_mode == Z80_IMMED_INDIRECT) {
+			copy.immed = 0x1000 + (rand() % 256 - 128);
+		}
+	}
+	uint8_t is_mem = 0;
+	uint16_t address;
+	int16_t offset;
+	switch(addr_mode)
+	{
+	case Z80_REG:
+		if (word_sized) {
+			reg_values[inst->ea_reg] = rand() % 65536;
+			reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8;
+			reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF;
+		} else {
+			reg_values[inst->ea_reg] = rand() % 256;
+			uint8_t word_reg = z80_word_reg(inst->ea_reg);
+			if (word_reg != Z80_UNUSED) {
+				reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
+			}
+		}
+		break;
+	case Z80_REG_INDIRECT:
+		is_mem = 1;
+		reg_values[inst->ea_reg] = 0x1000 + (rand() % 256 - 128);
+		address = reg_values[inst->ea_reg];
+		reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8;
+		reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF;
+		break;
+	case Z80_IMMED_INDIRECT:
+		is_mem = 1;
+		address = inst->immed;
+		break;
+	case Z80_IX_DISPLACE:
+		reg_values[Z80_IX] = 0x1000;
+		reg_values[Z80_IXH] = 0x10;
+		reg_values[Z80_IXL] = 0;
+		is_mem = 1;
+		offset = inst->ea_reg;
+		if (offset > 0x7F) {
+			offset -= 256;
+		}
+		address = 0x1000 + offset;
+		break;
+	case Z80_IY_DISPLACE:
+		reg_values[Z80_IY] = 0x1000;
+		reg_values[Z80_IYH] = 0x10;
+		reg_values[Z80_IYL] = 0;
+		is_mem = 1;
+		offset = inst->ea_reg;
+		if (offset > 0x7F) {
+			offset -= 256;
+		}
+		address = 0x1000 + offset;
+		break;
+	}
+	if (inst->reg != Z80_UNUSED && inst->reg != Z80_USE_IMMED) {
+		if (word_sized) {
+			reg_values[inst->reg] = rand() % 65536;
+			reg_values[z80_high_reg(inst->reg)] = reg_values[inst->reg] >> 8;
+			reg_values[z80_low_reg(inst->reg)] = reg_values[inst->reg] & 0xFF;
+		} else {
+			reg_values[inst->reg] = rand() % 255;
+			uint8_t word_reg = z80_word_reg(inst->reg);
+			if (word_reg != Z80_UNUSED) {
+				reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
+			}
+		}
+	}
+	puts("--------------");
+	for (uint8_t reg = 0; reg < Z80_UNUSED; reg++) {
+		if (reg_values[reg]) {
+			printf("%s: %X\n", z80_regs[reg], reg_values[reg]);
+		}
+	}
+	char disbuf[80];
+	z80_disasm(inst, disbuf);
+	puts(disbuf);
+	char pathbuf[128];
+	sprintf(pathbuf, "ztests/%s", z80_mnemonics[inst->op]);
+	if (mkdir(pathbuf, 0777) != 0) {
+		if (errno != EEXIST) {
+			fprintf(stderr, "Failed to create directory %s\n", disbuf);
+			exit(1);
+		}
+	}
+	uint8_t prog[200];
+	uint8_t *cur = prog;
+	uint8_t mem_val;
+	//disable interrupts
+	*(cur++) = 0xF3;
+	//setup SP
+	cur = ld_ir16(cur, Z80_SP, 0x2000);
+	//setup memory
+	if (is_mem) {
+		mem_val = rand() % 256;
+		cur = ld_ir8(cur, Z80_A, mem_val);
+		cur = ld_amem(cur, address);
+	}
+	//setup AF
+	cur = ld_ir16(cur, Z80_BC, reg_values[Z80_A] << 8);
+	cur = push(cur, Z80_BC);
+	cur = pop(cur, Z80_AF);
+	
+	//setup other regs
+	for (uint8_t reg = Z80_BC; reg <= Z80_IY; reg++) {
+		if (reg != Z80_AF && reg != Z80_SP) {
+			cur = ld_ir16(cur, reg, reg_values[reg]);
+		}
+	}
+	
+	//copy instruction
+	memcpy(cur, instbuf, instlen);
+	cur += instlen;
+	
+	//immed/displacement byte(s)
+	if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) {
+		*(cur++) = inst->ea_reg;
+	} else if (addr_mode == Z80_IMMED & inst->op != Z80_IM) {
+		*(cur++) = inst->immed & 0xFF;
+		if (word_sized) {
+			*(cur++) = inst->immed >> 8;
+		}
+	} else if (addr_mode == Z80_IMMED_INDIRECT) {
+		*(cur++) = inst->immed & 0xFF;
+		*(cur++) = inst->immed >> 8;
+	}
+	if (inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) {
+		*(cur++) = inst->immed & 0xFF;
+	}
+
+	for (char * cur = disbuf; *cur != 0; cur++) {
+		if (*cur == ',' || *cur == ' ') {
+			*cur = '_';
+		}
+	}
+	//halt
+	*(cur++) = 0x76;
+	sprintf(pathbuf + strlen(pathbuf), "/%s.bin", disbuf);
+	FILE * progfile = fopen(pathbuf, "wb");
+	fwrite(prog, 1, cur - prog, progfile);
+	fclose(progfile);
+}
+
+
+uint8_t should_skip(z80inst * inst)
+{
+	return inst->op >= Z80_JP || (inst->op >= Z80_LDI && inst->op <= Z80_CPDR) || inst->op == Z80_HALT 
+		|| inst->op == Z80_DAA || inst->op == Z80_RLD || inst->op == Z80_RRD || inst->op == Z80_NOP
+		|| inst->op == Z80_DI || inst->op == Z80_EI;
+}
+
+void z80_gen_all()
+{
+	uint8_t inst[3];
+	for (int op = 0; op < 256; op++) {
+		inst[0] = op;
+		if (op == 0xCB) {
+			for (int subop = 0; subop < 256; subop++) {
+				if (!should_skip(z80_tbl_bit + subop)) {
+					inst[1] = subop;
+					z80_gen_test(z80_tbl_bit + subop, inst, 2);
+				}
+			}
+		} else if(op == 0xDD) {
+			for (int ixop = 0; ixop < 256; ixop++) {
+				inst[1] = ixop;
+				if (ixop == 0xCB) {
+					for (int subop = 0; subop < 256; subop++) {
+						if (!should_skip(z80_tbl_ix_bit + subop)) {
+							inst[2] = subop;
+							z80_gen_test(z80_tbl_ix_bit + subop, inst, 3);
+						}
+					}
+				} else {
+					if (!should_skip(z80_tbl_ix + ixop)) {
+						z80_gen_test(z80_tbl_ix + ixop, inst, 2);
+					}
+				}
+			}
+		} else if(op == 0xED) {
+			for (int subop = 0; subop < sizeof(z80_tbl_extd)/sizeof(z80inst); subop++) {
+				if (!should_skip(z80_tbl_extd + subop)) {
+					inst[1] = subop;
+					z80_gen_test(z80_tbl_extd + subop, inst, 2);
+				}
+			}
+		} else if(op == 0xFD) {
+			for (int iyop = 0; iyop < 256; iyop++) {
+				inst[1] = iyop;
+				if (iyop == 0xCB) {
+					for (int subop = 0; subop < 256; subop++) {
+						if (!should_skip(z80_tbl_iy_bit + subop)) {
+							inst[2] = subop;
+							z80_gen_test(z80_tbl_iy_bit + subop, inst, 3);
+						}
+					}
+				} else {
+					if (!should_skip(z80_tbl_iy + iyop)) {
+						z80_gen_test(z80_tbl_iy + iyop, inst, 2);
+					}
+				}
+			}
+		} else {
+			if (!should_skip(z80_tbl_a + op)) {
+				z80_gen_test(z80_tbl_a + op, inst, 1);
+			}
+		}
+	}
+}
+
+int main(int argc, char ** argv)
+{
+	z80_gen_all();
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ztestrun.c	Wed May 08 14:40:48 2013 -0700
@@ -0,0 +1,92 @@
+#include "z80inst.h"
+#include "z80_to_x86.h"
+#include "mem.h"
+#include "vdp.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+uint8_t z80_ram[0x2000];
+uint16_t cart[0x200000];
+
+#define MCLKS_PER_Z80 15
+//TODO: Figure out the exact value for this
+#define MCLKS_PER_FRAME (MCLKS_LINE*262)
+#define VINT_CYCLE ((MCLKS_LINE * 226)/MCLKS_PER_Z80)
+#define CYCLE_NEVER 0xFFFFFFFF
+
+uint8_t z80_read_ym(uint16_t location, z80_context * context)
+{
+	return 0xFF;
+}
+
+z80_context * z80_write_ym(uint16_t location, z80_context * context, uint8_t value)
+{
+	return context;
+}
+
+int main(int argc, char ** argv)
+{
+	long filesize;
+	uint8_t *filebuf;
+	x86_z80_options opts;
+	z80_context context;
+	if (argc < 2) {
+		fputs("usage: transz80 zrom [cartrom]\n", stderr);
+		exit(1);
+	}
+	FILE * f = fopen(argv[1], "rb");
+	if (!f) {
+		fprintf(stderr, "unable to open file %s\n", argv[2]);
+		exit(1);
+	}
+	fseek(f, 0, SEEK_END);
+	filesize = ftell(f);
+	fseek(f, 0, SEEK_SET);
+	fread(z80_ram, 1, filesize < sizeof(z80_ram) ? filesize : sizeof(z80_ram), f);
+	fclose(f);
+	if (argc > 2) {
+		f = fopen(argv[2], "rb");
+		if (!f) {
+			fprintf(stderr, "unable to open file %s\n", argv[2]);
+			exit(1);
+		}
+		fseek(f, 0, SEEK_END);
+		filesize = ftell(f);
+		fseek(f, 0, SEEK_SET);
+		fread(cart, 1, filesize < sizeof(cart) ? filesize : sizeof(cart), f);
+		fclose(f);
+		for(unsigned short * cur = cart; cur - cart < (filesize/2); ++cur)
+		{
+			*cur = (*cur >> 8) | (*cur << 8);
+		}
+	}
+	init_x86_z80_opts(&opts);
+	init_z80_context(&context, &opts);
+	//Z80 RAM
+	context.mem_pointers[0] = z80_ram;
+	context.sync_cycle = context.target_cycle = 1000;
+	context.int_cycle = CYCLE_NEVER;
+	//cartridge/bank
+	context.mem_pointers[1] = context.mem_pointers[2] = (uint8_t *)cart;
+	z80_reset(&context);
+	while (context.current_cycle < 1000) {
+		z80_run(&context);
+	}
+	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\nSP: %X\n\nIM: %d, IFF1: %d, IFF2: %d\n", 
+		context.regs[Z80_A], context.regs[Z80_B], context.regs[Z80_C],
+		context.regs[Z80_D], context.regs[Z80_E], 
+		(context.regs[Z80_H] << 8) | context.regs[Z80_L], 
+		(context.regs[Z80_IXH] << 8) | context.regs[Z80_IXL], 
+		(context.regs[Z80_IYH] << 8) | context.regs[Z80_IYL], 
+		context.sp, context.im, context.iff1, context.iff2);
+	printf("Flags: SZVNC\n"
+	       "       %d%d%d%d%d\n", context.flags[ZF_S], context.flags[ZF_Z], context.flags[ZF_PV], context.flags[ZF_N], context.flags[ZF_C]);
+	puts("--Alternate Regs--");
+	printf("A: %X\nB: %X\nC: %X\nD: %X\nE: %X\nHL: %X\nIX: %X\nIY: %X\n", 
+		context.alt_regs[Z80_A], context.alt_regs[Z80_B], context.alt_regs[Z80_C],
+		context.alt_regs[Z80_D], context.alt_regs[Z80_E], 
+		(context.alt_regs[Z80_H] << 8) | context.alt_regs[Z80_L], 
+		(context.alt_regs[Z80_IXH] << 8) | context.alt_regs[Z80_IXL], 
+		(context.alt_regs[Z80_IYH] << 8) | context.alt_regs[Z80_IYL]);
+	return 0;
+}