main ztestgen.c
  1/*
  2 Copyright 2013 Michael Pavone
  3 This file is part of BlastEm.
  4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
  5*/
  6#include "z80inst.h"
  7#include <stdlib.h>
  8#include <string.h>
  9#include <stdint.h>
 10#include <stdio.h>
 11#include <sys/stat.h>
 12#include <sys/types.h>
 13#include <errno.h>
 14#include <time.h>
 15
 16extern z80inst z80_tbl_a[256];
 17extern z80inst z80_tbl_extd[0xC0-0x40];
 18extern z80inst z80_tbl_bit[256];
 19extern z80inst z80_tbl_ix[256];
 20extern z80inst z80_tbl_iy[256];
 21extern z80inst z80_tbl_ix_bit[256];
 22extern z80inst z80_tbl_iy_bit[256];
 23extern char *z80_mnemonics[Z80_OTDR+1];
 24extern char * z80_regs[Z80_USE_IMMED];
 25#define PRE_IX  0xDD
 26#define PRE_IY  0xFD
 27#define LD_IR16 0x01
 28#define INC_R8  0x04
 29#define LD_IR8  0x06
 30#define LD_RR8  0x40
 31#define AND_R   0xA0
 32#define PUSH    0xC5
 33#define POP     0xC1
 34
 35uint8_t * ld_ir16(uint8_t * dst, uint8_t reg, uint16_t val)
 36{
 37	if (reg == Z80_IX) {
 38		*(dst++) = PRE_IX;
 39		return ld_ir16(dst, Z80_HL, val);
 40	} else if(reg == Z80_IY) {
 41		*(dst++) = PRE_IY;
 42		return ld_ir16(dst, Z80_HL, val);
 43	} else {
 44		*(dst++) = LD_IR16 | ((reg - Z80_BC) << 4);
 45		*(dst++) = val & 0xFF;
 46		*(dst++) = val >> 8;
 47		return dst;
 48	}
 49}
 50
 51uint8_t * ld_ir8(uint8_t * dst, uint8_t reg, uint8_t val)
 52{
 53	if (reg <= Z80_H) {
 54		reg = (reg - Z80_C) ^ 1;
 55	} else {
 56		reg = 0x7;
 57	}
 58	*(dst++) = LD_IR8 | (reg << 3);
 59	*(dst++) = val;
 60	return dst;
 61}
 62
 63uint8_t * ld_rr8(uint8_t * dst, uint8_t src, uint8_t dstr)
 64{
 65	if (src <= Z80_H) {
 66		src = (src - Z80_C) ^ 1;
 67	} else {
 68		src = 0x7;
 69	}
 70	if (dstr <= Z80_H) {
 71		dstr = (dstr - Z80_C) ^ 1;
 72	} else {
 73		dstr = 0x7;
 74	}
 75	*(dst++) = LD_RR8 | (dstr << 3) | src;
 76	return dst;
 77}
 78
 79uint8_t * ld_amem(uint8_t * dst, uint16_t address)
 80{
 81	*(dst++) = 0x32;
 82	*(dst++) = address & 0xFF;
 83	*(dst++) = address >> 8;
 84	return dst;
 85}
 86
 87uint8_t * ld_mema(uint8_t * dst, uint16_t address)
 88{
 89	*(dst++) = 0x3A;
 90	*(dst++) = address & 0xFF;
 91	*(dst++) = address >> 8;
 92	return dst;
 93}
 94
 95uint8_t * push(uint8_t * dst, uint8_t reg)
 96{
 97	if (reg == Z80_IX) {
 98		*(dst++) = PRE_IX;
 99		return push(dst, Z80_HL);
100	} else if(reg == Z80_IY) {
101		*(dst++) = PRE_IY;
102		return push(dst, Z80_HL);
103	} else {
104		if (reg == Z80_AF) {
105			reg--;
106		}
107		*(dst++) = PUSH | ((reg - Z80_BC) << 4);
108		return dst;
109	}
110}
111
112uint8_t * pop(uint8_t * dst, uint8_t reg)
113{
114	if (reg == Z80_IX) {
115		*(dst++) = PRE_IX;
116		return pop(dst, Z80_HL);
117	} else if(reg == Z80_IY) {
118		*(dst++) = PRE_IY;
119		return pop(dst, Z80_HL);
120	} else {
121		if (reg == Z80_AF) {
122			reg--;
123		}
124		*(dst++) = POP | ((reg - Z80_BC) << 4);
125		return dst;
126	}
127}
128
129uint8_t * and_r(uint8_t * dst, uint8_t reg)
130{
131	if (reg == Z80_IXH || reg == Z80_IXL) {
132		*(dst++) = PRE_IX;
133		return and_r(dst, reg - (Z80_IXL - Z80_L));
134	} else if(reg == Z80_IYH || reg == Z80_IYL) {
135		*(dst++) = PRE_IY;
136		return and_r(dst, reg - (Z80_IYL - Z80_L));
137	} else {
138		if (reg == Z80_A) {
139			reg = 7;
140		} else {
141			reg = (reg - Z80_C) ^ 1;
142		}
143		*(dst++) = AND_R | reg;
144		return dst;
145	}
146}
147
148uint8_t * inc_r(uint8_t *dst, uint8_t reg)
149{
150	if (reg == Z80_IXH || reg == Z80_IXL) {
151		*(dst++) = PRE_IX;
152		return inc_r(dst, reg - (Z80_IXL - Z80_L));
153	} else if(reg == Z80_IYH || reg == Z80_IYL) {
154		*(dst++) = PRE_IY;
155		return inc_r(dst, reg - (Z80_IYL - Z80_L));
156	} else {
157		*(dst++) = INC_R8 | reg << 3;
158		return dst;
159	}
160}
161
162void mark_used8(uint8_t *reg_usage, uint16_t *reg_values, uint8_t reg, uint8_t init_value)
163{
164	reg_usage[reg] = 1;
165	reg_values[reg] = init_value;
166	uint8_t word_reg = z80_word_reg(reg);
167	if (word_reg != Z80_UNUSED) {
168		reg_usage[word_reg] = 1;
169		reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
170	}
171}
172
173uint8_t alloc_reg8(uint8_t *reg_usage, uint16_t *reg_values, uint8_t init_value)
174{
175	for (uint8_t reg = 0; reg < Z80_BC; reg++)
176	{
177		if (!reg_usage[reg]) {
178			mark_used8(reg_usage, reg_values, reg, init_value);
179			return reg;
180		}
181	}
182	return Z80_UNUSED;
183}
184
185void z80_gen_test(z80inst * inst, uint8_t *instbuf, uint8_t instlen)
186{
187	z80inst copy;
188	uint16_t reg_values[Z80_UNUSED];
189	uint8_t reg_usage[Z80_UNUSED];
190	memset(reg_values, 0, sizeof(reg_values));
191	memset(reg_usage, 0, sizeof(reg_usage));
192	uint8_t addr_mode = inst->addr_mode & 0x1F;
193	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;
194
195	if (inst->reg == Z80_USE_IMMED || addr_mode == Z80_IMMED || addr_mode == Z80_IMMED_INDIRECT
196		|| addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE)
197	{
198		memcpy(&copy, inst, sizeof(copy));
199		inst = &copy;
200		if ((inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET)
201			|| (addr_mode == Z80_IMMED && inst->op != Z80_IM))
202		{
203			copy.immed = rand() % (word_sized ? 65536 : 256);
204		}
205		if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) {
206			copy.ea_reg = rand() % 256;
207		}
208		if (addr_mode == Z80_IMMED_INDIRECT) {
209			copy.immed = 0x1000 + (rand() % 256 - 128);
210		}
211	}
212	uint8_t is_mem = 0;
213	uint16_t address;
214	int16_t offset;
215	switch(addr_mode)
216	{
217	case Z80_REG:
218		reg_usage[inst->ea_reg] = 1;
219		if (word_sized) {
220			reg_values[inst->ea_reg] = rand() % 65536;
221			reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8;
222			reg_usage[z80_high_reg(inst->ea_reg)] = 1;
223			reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF;
224			reg_usage[z80_low_reg(inst->ea_reg)] = 1;
225		} else {
226			mark_used8(reg_usage, reg_values, inst->ea_reg, rand() % 256);
227		}
228		break;
229	case Z80_REG_INDIRECT:
230		is_mem = 1;
231		reg_values[inst->ea_reg] = 0x1000 + (rand() % 256 - 128);
232		reg_usage[inst->ea_reg] = 1;
233		address = reg_values[inst->ea_reg];
234		reg_usage[z80_high_reg(inst->ea_reg)] = 1;
235		reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8;
236		reg_usage[z80_low_reg(inst->ea_reg)] = 1;
237		reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF;
238		break;
239	case Z80_IMMED_INDIRECT:
240		is_mem = 1;
241		address = inst->immed;
242		break;
243	case Z80_IX_DISPLACE:
244		reg_values[Z80_IX] = 0x1000;
245		reg_usage[Z80_IX] = 1;
246		reg_values[Z80_IXH] = 0x10;
247		reg_usage[Z80_IXH] = 1;
248		reg_values[Z80_IXL] = 0;
249		reg_usage[Z80_IXL] = 1;
250		is_mem = 1;
251		offset = inst->ea_reg;
252		if (offset > 0x7F) {
253			offset -= 256;
254		}
255		address = 0x1000 + offset;
256		break;
257	case Z80_IY_DISPLACE:
258		reg_values[Z80_IY] = 0x1000;
259		reg_usage[Z80_IY] = 1;
260		reg_values[Z80_IYH] = 0x10;
261		reg_usage[Z80_IYH] = 1;
262		reg_values[Z80_IYL] = 0;
263		reg_usage[Z80_IYL] = 1;
264		is_mem = 1;
265		offset = inst->ea_reg;
266		if (offset > 0x7F) {
267			offset -= 256;
268		}
269		address = 0x1000 + offset;
270		break;
271	}
272	if (inst->reg != Z80_UNUSED && inst->reg != Z80_USE_IMMED) {
273
274		if (word_sized) {
275			reg_values[inst->reg] = rand() % 65536;
276			reg_usage[z80_high_reg(inst->reg)] = 1;
277			reg_values[z80_high_reg(inst->reg)] = reg_values[inst->reg] >> 8;
278			reg_usage[z80_low_reg(inst->reg)] = 1;
279			reg_values[z80_low_reg(inst->reg)] = reg_values[inst->reg] & 0xFF;
280		} else {
281			if (!reg_usage[inst->reg]) {
282				reg_values[inst->reg] = rand() % 255;
283				uint8_t word_reg = z80_word_reg(inst->reg);
284				if (word_reg != Z80_UNUSED) {
285					reg_usage[word_reg] = 1;
286					reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF);
287				}
288			}
289		}
290		reg_usage[inst->reg] = 1;
291	}
292	uint8_t counter_reg = Z80_UNUSED;
293	if (inst->op >= Z80_JP && inst->op <= Z80_JRCC) {
294		counter_reg = alloc_reg8(reg_usage, reg_values, 0);
295	}
296	puts("--------------");
297	for (uint8_t reg = 0; reg < Z80_UNUSED; reg++) {
298		if (reg_values[reg]) {
299			printf("%s: %X\n", z80_regs[reg], reg_values[reg]);
300		}
301	}
302	char disbuf[80];
303	z80_disasm(inst, disbuf, 0);
304	puts(disbuf);
305	char pathbuf[128];
306	sprintf(pathbuf, "ztests/%s", z80_mnemonics[inst->op]);
307	if (mkdir(pathbuf, 0777) != 0) {
308		if (errno != EEXIST) {
309			fprintf(stderr, "Failed to create directory %s\n", disbuf);
310			exit(1);
311		}
312	}
313	uint8_t prog[200];
314	uint8_t *cur = prog;
315	uint8_t mem_val;
316	//disable interrupts
317	*(cur++) = 0xF3;
318	//setup SP
319	cur = ld_ir16(cur, Z80_SP, 0x2000);
320	for (int i = 0; i < 2; i ++) {
321		//setup memory
322		if (is_mem) {
323			mem_val = rand() % 256;
324			cur = ld_ir8(cur, Z80_A, mem_val);
325			cur = ld_amem(cur, address);
326		}
327		//setup AF
328		cur = ld_ir16(cur, Z80_BC, reg_values[Z80_A] << 8 | (i ? 0xFF : 0));
329		cur = push(cur, Z80_BC);
330		cur = pop(cur, Z80_AF);
331
332		//setup other regs
333		for (uint8_t reg = Z80_BC; reg <= Z80_IY; reg++) {
334			if (reg != Z80_AF && reg != Z80_SP && (inst->op != Z80_JP || addr_mode != Z80_REG_INDIRECT || inst->ea_reg != reg)) {
335				if (i == 1 && (z80_high_reg(reg) == counter_reg || z80_low_reg(reg) == counter_reg)) {
336					if (z80_high_reg(reg) == counter_reg) {
337						if (reg_usage[z80_low_reg(reg)]) {
338							cur = ld_ir8(cur, z80_low_reg(reg), reg_values[z80_low_reg(reg)]);
339						}
340					} else if (reg_usage[z80_high_reg(reg)]) {
341						cur = ld_ir8(cur, z80_high_reg(reg), reg_values[z80_high_reg(reg)]);
342					}
343				} else {
344					cur = ld_ir16(cur, reg, reg_values[reg]);
345				}
346			}
347		}
348
349		if (inst->op == Z80_JP && addr_mode == Z80_REG_INDIRECT) {
350			uint16_t address = cur - prog + (inst->ea_reg == Z80_HL ? 3 : 4) + instlen + 1 + i;
351			cur = ld_ir16(cur, inst->ea_reg, address);
352		}
353
354		//copy instruction
355		if (instlen == 3) {
356			memcpy(cur, instbuf, 2);
357			cur += 2;
358		} else {
359			memcpy(cur, instbuf, instlen);
360			cur += instlen;
361		}
362
363		//immed/displacement byte(s)
364		if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) {
365			*(cur++) = inst->ea_reg;
366		} else if ((inst->op == Z80_JP || inst->op == Z80_JPCC) && addr_mode == Z80_IMMED) {
367			uint16_t address = cur - prog + 3 + i; //2 for immed address, 1/2 for instruction(s) to skip
368			*(cur++) = address;
369			*(cur++) = address >> 8;
370		} else if(inst->op == Z80_JR || inst->op == Z80_JRCC) {
371			*(cur++) = 1 + i; //skip one or 2 instructions based on value of i
372		} else if (addr_mode == Z80_IMMED & inst->op != Z80_IM) {
373			*(cur++) = inst->immed & 0xFF;
374			if (word_sized) {
375				*(cur++) = inst->immed >> 8;
376			}
377		} else if (addr_mode == Z80_IMMED_INDIRECT) {
378			*(cur++) = inst->immed & 0xFF;
379			*(cur++) = inst->immed >> 8;
380		}
381		if (inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) {
382			*(cur++) = inst->immed & 0xFF;
383		}
384		if (instlen == 3) {
385			*(cur++) = instbuf[2];
386		}
387		if (inst->op >= Z80_JP && inst->op <= Z80_JRCC) {
388			cur = inc_r(cur, counter_reg);
389			if (i) {
390				//inc twice on second iteration so we can differentiate the two
391				cur = inc_r(cur, counter_reg);
392			}
393		}
394		if (!i) {
395			//Save AF from first run
396			cur = push(cur, Z80_AF);
397			if (is_mem) {
398				//Save memory location from frist run
399				cur = ld_mema(cur, address);
400				cur = push(cur, Z80_AF);
401			}
402		} else {
403			//Pop AF from first run for final result
404			for (int reg = Z80_BC; reg <= Z80_IY; reg++) {
405				if (reg != Z80_AF && !reg_usage[reg]) {
406					cur = pop(cur, reg);
407					cur = push(cur, Z80_AF);
408					cur = ld_ir8(cur, Z80_A, 0xC7);
409					cur = and_r(cur, z80_low_reg(reg));
410					cur = ld_rr8(cur, Z80_A, z80_low_reg(reg));
411					cur = pop(cur, Z80_AF);
412					reg_usage[reg] = 1;
413					reg_usage[z80_low_reg(reg)] = 1;
414					break;
415				}
416			}
417			if (is_mem) {
418				//Pop memory location from frist run
419				for (int reg = Z80_BC; reg <= Z80_IY; reg++) {
420					if (reg != Z80_AF && !reg_usage[reg]) {
421						cur = pop(cur, reg);
422						cur = push(cur, Z80_AF);
423						cur = ld_mema(cur, address);
424						cur = ld_rr8(cur, Z80_A, z80_low_reg(reg));
425						cur = pop(cur, Z80_AF);
426						reg_usage[reg] = 1;
427						reg_usage[z80_low_reg(reg)] = 1;
428						break;
429					}
430				}
431			}
432		}
433	}
434
435	for (char * cur = disbuf; *cur != 0; cur++) {
436		if (*cur == ',' || *cur == ' ') {
437			*cur = '_';
438		}
439	}
440	//save memory result
441	if (is_mem) {
442		if (reg_usage[Z80_A]) {
443			cur = push(cur, Z80_AF);
444		}
445		cur = ld_mema(cur, address);
446		if (reg_usage[Z80_A]) {
447			for (int reg = 0; reg < Z80_I; reg++) {
448				if (!reg_usage[reg]) {
449					cur = ld_rr8(cur, Z80_A, reg);
450					break;
451				}
452			}
453			cur = pop(cur, Z80_AF);
454		}
455	}
456
457	//halt
458	*(cur++) = 0x76;
459	sprintf(pathbuf + strlen(pathbuf), "/%s.bin", disbuf);
460	FILE * progfile = fopen(pathbuf, "wb");
461	fwrite(prog, 1, cur - prog, progfile);
462	fclose(progfile);
463}
464
465
466uint8_t should_skip(z80inst * inst)
467{
468	return inst->op >= Z80_DJNZ || (inst->op >= Z80_LDI && inst->op <= Z80_CPDR) || inst->op == Z80_HALT
469		|| inst->op == Z80_RLD || inst->op == Z80_RRD || inst->op == Z80_NOP
470		|| inst->op == Z80_DI || inst->op == Z80_EI;
471}
472
473void z80_gen_all()
474{
475	uint8_t inst[3];
476	for (int op = 0; op < 256; op++) {
477		inst[0] = op;
478		if (op == 0xCB) {
479			for (int subop = 0; subop < 256; subop++) {
480				if (!should_skip(z80_tbl_bit + subop)) {
481					inst[1] = subop;
482					z80_gen_test(z80_tbl_bit + subop, inst, 2);
483				}
484			}
485		} else if(op == 0xDD) {
486			for (int ixop = 0; ixop < 256; ixop++) {
487				inst[1] = ixop;
488				if (ixop == 0xCB) {
489					for (int subop = 0; subop < 256; subop++) {
490						if (!should_skip(z80_tbl_ix_bit + subop)) {
491							inst[2] = subop;
492							z80_gen_test(z80_tbl_ix_bit + subop, inst, 3);
493						}
494					}
495				} else {
496					if (!should_skip(z80_tbl_ix + ixop)) {
497						z80_gen_test(z80_tbl_ix + ixop, inst, 2);
498					}
499				}
500			}
501		} else if(op == 0xED) {
502			for (int subop = 0; subop < sizeof(z80_tbl_extd)/sizeof(z80inst); subop++) {
503				if (!should_skip(z80_tbl_extd + subop)) {
504					inst[1] = subop + 0x40;
505					z80_gen_test(z80_tbl_extd + subop, inst, 2);
506				}
507			}
508		} else if(op == 0xFD) {
509			for (int iyop = 0; iyop < 256; iyop++) {
510				inst[1] = iyop;
511				if (iyop == 0xCB) {
512					for (int subop = 0; subop < 256; subop++) {
513						if (!should_skip(z80_tbl_iy_bit + subop)) {
514							inst[2] = subop;
515							z80_gen_test(z80_tbl_iy_bit + subop, inst, 3);
516						}
517					}
518				} else {
519					if (!should_skip(z80_tbl_iy + iyop)) {
520						z80_gen_test(z80_tbl_iy + iyop, inst, 2);
521					}
522				}
523			}
524		} else {
525			if (!should_skip(z80_tbl_a + op)) {
526				z80_gen_test(z80_tbl_a + op, inst, 1);
527			}
528		}
529	}
530}
531
532int main(int argc, char ** argv)
533{
534	srand(time(NULL));
535	z80_gen_all();
536	return 0;
537}