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(©, inst, sizeof(copy));
199 inst = ©
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}