Mercurial > repos > blastem
diff ztestgen.c @ 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 | |
children | f90aeea98e53 |
line wrap: on
line diff
--- /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(©, inst, sizeof(copy)); + inst = © + 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; +}