Mercurial > repos > blastem
comparison 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 |
comparison
equal
deleted
inserted
replaced
291:eea3b118940d | 292:b970ea214ecb |
---|---|
1 #include "z80inst.h" | |
2 #include <stdlib.h> | |
3 #include <string.h> | |
4 #include <stdint.h> | |
5 #include <stdio.h> | |
6 #include <sys/stat.h> | |
7 #include <sys/types.h> | |
8 #include <errno.h> | |
9 | |
10 extern z80inst z80_tbl_a[256]; | |
11 extern z80inst z80_tbl_extd[0xC0-0x40]; | |
12 extern z80inst z80_tbl_bit[256]; | |
13 extern z80inst z80_tbl_ix[256]; | |
14 extern z80inst z80_tbl_iy[256]; | |
15 extern z80inst z80_tbl_ix_bit[256]; | |
16 extern z80inst z80_tbl_iy_bit[256]; | |
17 extern char *z80_mnemonics[Z80_OTDR+1]; | |
18 extern char * z80_regs[Z80_USE_IMMED]; | |
19 #define PRE_IX 0xDD | |
20 #define PRE_IY 0xFD | |
21 #define LD_IR16 0x01 | |
22 #define LD_IR8 0x06 | |
23 #define PUSH 0xC5 | |
24 #define POP 0xC1 | |
25 | |
26 uint8_t * ld_ir16(uint8_t * dst, uint8_t reg, uint16_t val) | |
27 { | |
28 if (reg == Z80_IX) { | |
29 *(dst++) = PRE_IX; | |
30 return ld_ir16(dst, Z80_HL, val); | |
31 } else if(reg == Z80_IY) { | |
32 *(dst++) = PRE_IY; | |
33 return ld_ir16(dst, Z80_HL, val); | |
34 } else { | |
35 *(dst++) = LD_IR16 | ((reg - Z80_BC) << 4); | |
36 *(dst++) = val & 0xFF; | |
37 *(dst++) = val >> 8; | |
38 return dst; | |
39 } | |
40 } | |
41 | |
42 uint8_t * ld_ir8(uint8_t * dst, uint8_t reg, uint8_t val) | |
43 { | |
44 if (reg <= Z80_H) { | |
45 reg = (reg - Z80_C) ^ 1; | |
46 } else { | |
47 reg = 0x7; | |
48 } | |
49 *(dst++) = LD_IR8 | (reg << 3); | |
50 *(dst++) = val; | |
51 return dst; | |
52 } | |
53 | |
54 uint8_t * ld_amem(uint8_t * dst, uint16_t address) | |
55 { | |
56 *(dst++) = 0x32; | |
57 *(dst++) = address & 0xFF; | |
58 *(dst++) = address >> 8; | |
59 return dst; | |
60 } | |
61 | |
62 uint8_t * push(uint8_t * dst, uint8_t reg) | |
63 { | |
64 if (reg == Z80_IX) { | |
65 *(dst++) = PRE_IX; | |
66 return push(dst, Z80_HL); | |
67 } else if(reg == Z80_IY) { | |
68 *(dst++) = PRE_IY; | |
69 return push(dst, Z80_HL); | |
70 } else { | |
71 if (reg == Z80_AF) { | |
72 reg--; | |
73 } | |
74 *(dst++) = PUSH | ((reg - Z80_BC) << 4); | |
75 return dst; | |
76 } | |
77 } | |
78 | |
79 uint8_t * pop(uint8_t * dst, uint8_t reg) | |
80 { | |
81 if (reg == Z80_IX) { | |
82 *(dst++) = PRE_IX; | |
83 return pop(dst, Z80_HL); | |
84 } else if(reg == Z80_IY) { | |
85 *(dst++) = PRE_IY; | |
86 return pop(dst, Z80_HL); | |
87 } else { | |
88 if (reg == Z80_AF) { | |
89 reg--; | |
90 } | |
91 *(dst++) = POP | ((reg - Z80_BC) << 4); | |
92 return dst; | |
93 } | |
94 } | |
95 | |
96 void z80_gen_test(z80inst * inst, uint8_t *instbuf, uint8_t instlen) | |
97 { | |
98 z80inst copy; | |
99 uint16_t reg_values[Z80_UNUSED]; | |
100 memset(reg_values, 0, sizeof(reg_values)); | |
101 uint8_t addr_mode = inst->addr_mode & 0x1F; | |
102 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; | |
103 | |
104 if (inst->reg == Z80_USE_IMMED || addr_mode == Z80_IMMED || addr_mode == Z80_IMMED_INDIRECT | |
105 || addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) | |
106 { | |
107 memcpy(©, inst, sizeof(copy)); | |
108 inst = © | |
109 if ((inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) | |
110 || (addr_mode == Z80_IMMED && inst->op != Z80_IM)) | |
111 { | |
112 copy.immed = rand() % (word_sized ? 65536 : 256); | |
113 } | |
114 if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) { | |
115 copy.ea_reg = rand() % 256; | |
116 } | |
117 if (addr_mode == Z80_IMMED_INDIRECT) { | |
118 copy.immed = 0x1000 + (rand() % 256 - 128); | |
119 } | |
120 } | |
121 uint8_t is_mem = 0; | |
122 uint16_t address; | |
123 int16_t offset; | |
124 switch(addr_mode) | |
125 { | |
126 case Z80_REG: | |
127 if (word_sized) { | |
128 reg_values[inst->ea_reg] = rand() % 65536; | |
129 reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8; | |
130 reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF; | |
131 } else { | |
132 reg_values[inst->ea_reg] = rand() % 256; | |
133 uint8_t word_reg = z80_word_reg(inst->ea_reg); | |
134 if (word_reg != Z80_UNUSED) { | |
135 reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF); | |
136 } | |
137 } | |
138 break; | |
139 case Z80_REG_INDIRECT: | |
140 is_mem = 1; | |
141 reg_values[inst->ea_reg] = 0x1000 + (rand() % 256 - 128); | |
142 address = reg_values[inst->ea_reg]; | |
143 reg_values[z80_high_reg(inst->ea_reg)] = reg_values[inst->ea_reg] >> 8; | |
144 reg_values[z80_low_reg(inst->ea_reg)] = reg_values[inst->ea_reg] & 0xFF; | |
145 break; | |
146 case Z80_IMMED_INDIRECT: | |
147 is_mem = 1; | |
148 address = inst->immed; | |
149 break; | |
150 case Z80_IX_DISPLACE: | |
151 reg_values[Z80_IX] = 0x1000; | |
152 reg_values[Z80_IXH] = 0x10; | |
153 reg_values[Z80_IXL] = 0; | |
154 is_mem = 1; | |
155 offset = inst->ea_reg; | |
156 if (offset > 0x7F) { | |
157 offset -= 256; | |
158 } | |
159 address = 0x1000 + offset; | |
160 break; | |
161 case Z80_IY_DISPLACE: | |
162 reg_values[Z80_IY] = 0x1000; | |
163 reg_values[Z80_IYH] = 0x10; | |
164 reg_values[Z80_IYL] = 0; | |
165 is_mem = 1; | |
166 offset = inst->ea_reg; | |
167 if (offset > 0x7F) { | |
168 offset -= 256; | |
169 } | |
170 address = 0x1000 + offset; | |
171 break; | |
172 } | |
173 if (inst->reg != Z80_UNUSED && inst->reg != Z80_USE_IMMED) { | |
174 if (word_sized) { | |
175 reg_values[inst->reg] = rand() % 65536; | |
176 reg_values[z80_high_reg(inst->reg)] = reg_values[inst->reg] >> 8; | |
177 reg_values[z80_low_reg(inst->reg)] = reg_values[inst->reg] & 0xFF; | |
178 } else { | |
179 reg_values[inst->reg] = rand() % 255; | |
180 uint8_t word_reg = z80_word_reg(inst->reg); | |
181 if (word_reg != Z80_UNUSED) { | |
182 reg_values[word_reg] = (reg_values[z80_high_reg(word_reg)] << 8) | (reg_values[z80_low_reg(word_reg)] & 0xFF); | |
183 } | |
184 } | |
185 } | |
186 puts("--------------"); | |
187 for (uint8_t reg = 0; reg < Z80_UNUSED; reg++) { | |
188 if (reg_values[reg]) { | |
189 printf("%s: %X\n", z80_regs[reg], reg_values[reg]); | |
190 } | |
191 } | |
192 char disbuf[80]; | |
193 z80_disasm(inst, disbuf); | |
194 puts(disbuf); | |
195 char pathbuf[128]; | |
196 sprintf(pathbuf, "ztests/%s", z80_mnemonics[inst->op]); | |
197 if (mkdir(pathbuf, 0777) != 0) { | |
198 if (errno != EEXIST) { | |
199 fprintf(stderr, "Failed to create directory %s\n", disbuf); | |
200 exit(1); | |
201 } | |
202 } | |
203 uint8_t prog[200]; | |
204 uint8_t *cur = prog; | |
205 uint8_t mem_val; | |
206 //disable interrupts | |
207 *(cur++) = 0xF3; | |
208 //setup SP | |
209 cur = ld_ir16(cur, Z80_SP, 0x2000); | |
210 //setup memory | |
211 if (is_mem) { | |
212 mem_val = rand() % 256; | |
213 cur = ld_ir8(cur, Z80_A, mem_val); | |
214 cur = ld_amem(cur, address); | |
215 } | |
216 //setup AF | |
217 cur = ld_ir16(cur, Z80_BC, reg_values[Z80_A] << 8); | |
218 cur = push(cur, Z80_BC); | |
219 cur = pop(cur, Z80_AF); | |
220 | |
221 //setup other regs | |
222 for (uint8_t reg = Z80_BC; reg <= Z80_IY; reg++) { | |
223 if (reg != Z80_AF && reg != Z80_SP) { | |
224 cur = ld_ir16(cur, reg, reg_values[reg]); | |
225 } | |
226 } | |
227 | |
228 //copy instruction | |
229 memcpy(cur, instbuf, instlen); | |
230 cur += instlen; | |
231 | |
232 //immed/displacement byte(s) | |
233 if (addr_mode == Z80_IX_DISPLACE || addr_mode == Z80_IY_DISPLACE) { | |
234 *(cur++) = inst->ea_reg; | |
235 } else if (addr_mode == Z80_IMMED & inst->op != Z80_IM) { | |
236 *(cur++) = inst->immed & 0xFF; | |
237 if (word_sized) { | |
238 *(cur++) = inst->immed >> 8; | |
239 } | |
240 } else if (addr_mode == Z80_IMMED_INDIRECT) { | |
241 *(cur++) = inst->immed & 0xFF; | |
242 *(cur++) = inst->immed >> 8; | |
243 } | |
244 if (inst->reg == Z80_USE_IMMED && inst->op != Z80_BIT && inst->op != Z80_RES && inst->op != Z80_SET) { | |
245 *(cur++) = inst->immed & 0xFF; | |
246 } | |
247 | |
248 for (char * cur = disbuf; *cur != 0; cur++) { | |
249 if (*cur == ',' || *cur == ' ') { | |
250 *cur = '_'; | |
251 } | |
252 } | |
253 //halt | |
254 *(cur++) = 0x76; | |
255 sprintf(pathbuf + strlen(pathbuf), "/%s.bin", disbuf); | |
256 FILE * progfile = fopen(pathbuf, "wb"); | |
257 fwrite(prog, 1, cur - prog, progfile); | |
258 fclose(progfile); | |
259 } | |
260 | |
261 | |
262 uint8_t should_skip(z80inst * inst) | |
263 { | |
264 return inst->op >= Z80_JP || (inst->op >= Z80_LDI && inst->op <= Z80_CPDR) || inst->op == Z80_HALT | |
265 || inst->op == Z80_DAA || inst->op == Z80_RLD || inst->op == Z80_RRD || inst->op == Z80_NOP | |
266 || inst->op == Z80_DI || inst->op == Z80_EI; | |
267 } | |
268 | |
269 void z80_gen_all() | |
270 { | |
271 uint8_t inst[3]; | |
272 for (int op = 0; op < 256; op++) { | |
273 inst[0] = op; | |
274 if (op == 0xCB) { | |
275 for (int subop = 0; subop < 256; subop++) { | |
276 if (!should_skip(z80_tbl_bit + subop)) { | |
277 inst[1] = subop; | |
278 z80_gen_test(z80_tbl_bit + subop, inst, 2); | |
279 } | |
280 } | |
281 } else if(op == 0xDD) { | |
282 for (int ixop = 0; ixop < 256; ixop++) { | |
283 inst[1] = ixop; | |
284 if (ixop == 0xCB) { | |
285 for (int subop = 0; subop < 256; subop++) { | |
286 if (!should_skip(z80_tbl_ix_bit + subop)) { | |
287 inst[2] = subop; | |
288 z80_gen_test(z80_tbl_ix_bit + subop, inst, 3); | |
289 } | |
290 } | |
291 } else { | |
292 if (!should_skip(z80_tbl_ix + ixop)) { | |
293 z80_gen_test(z80_tbl_ix + ixop, inst, 2); | |
294 } | |
295 } | |
296 } | |
297 } else if(op == 0xED) { | |
298 for (int subop = 0; subop < sizeof(z80_tbl_extd)/sizeof(z80inst); subop++) { | |
299 if (!should_skip(z80_tbl_extd + subop)) { | |
300 inst[1] = subop; | |
301 z80_gen_test(z80_tbl_extd + subop, inst, 2); | |
302 } | |
303 } | |
304 } else if(op == 0xFD) { | |
305 for (int iyop = 0; iyop < 256; iyop++) { | |
306 inst[1] = iyop; | |
307 if (iyop == 0xCB) { | |
308 for (int subop = 0; subop < 256; subop++) { | |
309 if (!should_skip(z80_tbl_iy_bit + subop)) { | |
310 inst[2] = subop; | |
311 z80_gen_test(z80_tbl_iy_bit + subop, inst, 3); | |
312 } | |
313 } | |
314 } else { | |
315 if (!should_skip(z80_tbl_iy + iyop)) { | |
316 z80_gen_test(z80_tbl_iy + iyop, inst, 2); | |
317 } | |
318 } | |
319 } | |
320 } else { | |
321 if (!should_skip(z80_tbl_a + op)) { | |
322 z80_gen_test(z80_tbl_a + op, inst, 1); | |
323 } | |
324 } | |
325 } | |
326 } | |
327 | |
328 int main(int argc, char ** argv) | |
329 { | |
330 z80_gen_all(); | |
331 return 0; | |
332 } |