Rev 2259 | Details | Compare with Previous | Last modification | View Log
Rev | Author | Line No. | Line |
---|---|---|---|
2241 | lvd | 1 | // Z80 ciphers test framework |
2 | // (c) 2019 lvd^mhm |
||
3 | |||
4 | /* |
||
5 | This file is part of Z80 ciphers test framework. |
||
6 | |||
7 | Z80 ciphers test framework is free software: |
||
8 | you can redistribute it and/or modify it under the terms of |
||
9 | the GNU General Public License as published by |
||
10 | the Free Software Foundation, either version 3 of the License, or |
||
11 | (at your option) any later version. |
||
12 | |||
13 | Z80 ciphers test framework is distributed in the hope that |
||
14 | it will be useful, but WITHOUT ANY WARRANTY; without even |
||
15 | the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
16 | See the GNU General Public License for more details. |
||
17 | |||
18 | You should have received a copy of the GNU General Public License |
||
19 | along with Z80 ciphers test framework. |
||
20 | If not, see <http://www.gnu.org/licenses/>. |
||
21 | */ |
||
22 | |||
23 | #include <stdint.h> |
||
24 | #include <stdio.h> |
||
25 | #include <stdlib.h> |
||
26 | #include <string.h> |
||
27 | |||
28 | #include "zetaZ80/Z80_lite.h" |
||
29 | |||
30 | #include "z80-wrap.h" |
||
31 | |||
32 | |||
33 | |||
2258 | lvd | 34 | static void z80_finish(void * param) |
35 | { |
||
36 | struct z80_context * z80 = param; |
||
37 | |||
38 | fprintf(stdout,"\n<<<Finished in %ld clocks!>>>\n",z80->z80.cycles); |
||
39 | exit(0); |
||
40 | } |
||
41 | |||
2247 | lvd | 42 | static uint8_t z80_filter_fetch_opcode(void * param, uint16_t address) |
43 | { |
||
44 | struct z80_context * z80 = param; |
||
45 | uint8_t opcode = z80->z80_mem[address]; |
||
46 | |||
2259 | lvd | 47 | // fprintf(stderr,"<<< MP=%04x AF=%04x BC=%04x DE=%04x HL=%04x SP=%04x PC=%04x op=%02x\n",z80->z80.memptr,z80->z80.af,z80->z80.bc,z80->z80.de,z80->z80.hl,z80->z80.sp,z80->z80.pc,opcode); |
2247 | lvd | 48 | |
49 | return opcode; |
||
50 | } |
||
51 | |||
2254 | lvd | 52 | static uint8_t z80_filter_zx_api(void * param, uint16_t address) |
53 | { |
||
54 | struct z80_context * z80 = param; |
||
55 | |||
2258 | lvd | 56 | if( address==0 ) // finish? |
2254 | lvd | 57 | { |
2258 | lvd | 58 | z80_finish(param); |
59 | } |
||
60 | else if( address==0x1601 ) // CHAN-OPEN (ignore) |
||
61 | { |
||
2254 | lvd | 62 | return 0xC9; |
63 | } |
||
64 | else if( address==0x0010 ) // RST 0x10 (print character) |
||
65 | { |
||
66 | uint8_t c = ((z80->z80.af)>>8)&0xFF; |
||
67 | |||
68 | if( z80->was_23 ) // skip 2 bytes after 23 |
||
69 | { |
||
70 | if( z80->was_23==2 ) |
||
71 | { |
||
72 | fprintf(stdout,"\033[%dG",c); |
||
73 | } |
||
74 | |||
75 | z80->was_23--; |
||
76 | } |
||
77 | else if( c==13 ) |
||
78 | { |
||
79 | fprintf(stdout,"\n"); |
||
80 | } |
||
81 | else if( c==127 ) // skip |
||
82 | { |
||
83 | } |
||
84 | else if( c==23 ) // skip following 2 bytes |
||
85 | { |
||
86 | z80->was_23=2; |
||
87 | } |
||
88 | else |
||
89 | { |
||
90 | fprintf(stdout,"%c",c); |
||
91 | fflush(stdout); |
||
92 | } |
||
93 | |||
94 | return 0xC9; |
||
95 | } |
||
96 | else if( address<0x4000 ) // catch all other ROM accesses |
||
97 | { |
||
98 | fprintf(stderr,"Accessed addr %04x! [sp]=%04x\n",address,*((uint16_t *)&z80->z80_mem[z80->z80.sp])); |
||
99 | exit(1); |
||
100 | } |
||
101 | |||
102 | return z80_filter_fetch_opcode(param, address); |
||
103 | } |
||
104 | |||
2243 | lvd | 105 | static uint8_t z80_filter_nedoos_api(void * param, uint16_t address) |
106 | { |
||
107 | struct z80_context * z80 = param; |
||
108 | |||
109 | if( address==0 ) |
||
110 | { |
||
2258 | lvd | 111 | z80_finish(param); |
2243 | lvd | 112 | } |
113 | else if( address==5 ) |
||
114 | { |
||
115 | uint8_t c = (z80->z80.bc)&0xFF; |
||
116 | |||
117 | if( c==0xD3 // GETSTDINOUT |
||
118 | || c==0xFF // YIELDKEEP |
||
119 | ) |
||
120 | { |
||
121 | return 0xC9; // just ignore |
||
122 | } |
||
123 | else if( c==0x49 ) // WRITEHANDLE |
||
124 | { // in:B - handle (ignore) |
||
125 | // in:DE - buffer |
||
126 | // in:HL - bytes to write |
||
127 | // out:HL - actually written |
||
128 | // out:A - error if nonzero |
||
129 | |||
130 | uint16_t ptr = z80->z80.de; |
||
131 | uint16_t ctr = z80->z80.hl; |
||
132 | |||
133 | while( ctr-- ) |
||
134 | { |
||
135 | fprintf(stdout,"%c",z80->z80_mem[ptr++]); |
||
136 | } |
||
137 | if( z80->z80.hl ) fflush(stdout); |
||
138 | |||
139 | // HL unchanged |
||
140 | z80->z80.af &= 0x00FF; |
||
141 | |||
142 | return 0xC9; |
||
143 | } |
||
144 | else |
||
145 | { |
||
146 | fprintf(stderr,"\n<<unknown BDOS call: c=%02x!>>\n",c); |
||
147 | exit(1); |
||
148 | } |
||
149 | } |
||
150 | |||
2247 | lvd | 151 | return z80_filter_fetch_opcode(param, address); |
2243 | lvd | 152 | } |
153 | |||
2242 | lvd | 154 | static uint8_t z80_filter_cpm_api(void * param, uint16_t address) |
155 | { |
||
156 | struct z80_context * z80 = param; |
||
157 | |||
158 | if( address==0 ) |
||
159 | { |
||
2258 | lvd | 160 | z80_finish(param); |
2242 | lvd | 161 | } |
162 | else if( address==5 ) |
||
163 | { |
||
164 | uint8_t c = (z80->z80.bc)&0xFF; |
||
165 | |||
166 | if( c==2 ) // print char in e |
||
167 | { |
||
168 | fprintf(stdout,"%c",(z80->z80.de)&0xFF); |
||
169 | fflush(stdout); |
||
170 | } |
||
171 | else if( c==9 ) // print string pointed to by de |
||
172 | { |
||
173 | uint16_t ptr = z80->z80.de; |
||
174 | uint8_t chr; |
||
175 | |||
176 | while( (chr=z80->z80_mem[ptr++]) != '$' ) |
||
177 | { |
||
178 | fprintf(stdout,"%c",chr); |
||
179 | } |
||
180 | fflush(stdout); |
||
181 | } |
||
182 | else |
||
183 | { |
||
184 | fprintf(stderr,"\n<<<Unknown CP/M API call: C=%02x>>>\n", c); |
||
185 | exit(1); |
||
186 | } |
||
187 | |||
188 | return 0xC9; |
||
189 | } |
||
190 | |||
2247 | lvd | 191 | return z80_filter_fetch_opcode(param, address); |
2242 | lvd | 192 | } |
193 | |||
2241 | lvd | 194 | static uint8_t z80_rd(void * param, uint16_t address) |
195 | { |
||
196 | struct z80_context * z80 = param; |
||
197 | |||
198 | return z80->z80_mem[address]; |
||
199 | } |
||
200 | |||
2254 | lvd | 201 | static void z80_zx_wr(void * param, uint16_t address, uint8_t data) |
202 | { |
||
203 | struct z80_context * z80 = param; |
||
204 | |||
205 | if( address>=0x4000 ) z80->z80_mem[address] = data; |
||
206 | } |
||
207 | |||
2241 | lvd | 208 | static void z80_wr(void * param, uint16_t address, uint8_t data) |
209 | { |
||
210 | struct z80_context * z80 = param; |
||
211 | |||
212 | z80->z80_mem[address] = data; |
||
213 | } |
||
214 | |||
215 | static void z80_hlt(void * param, uint8_t state) |
||
216 | { |
||
217 | struct z80_context * z80 = param; |
||
218 | |||
219 | z80_break(&z80->z80); |
||
220 | } |
||
221 | |||
2266 | lvd | 222 | static void z80_out(void * param, uint16_t addr, uint8_t data) |
2254 | lvd | 223 | { |
224 | // ignore |
||
225 | } |
||
2241 | lvd | 226 | |
2254 | lvd | 227 | static uint8_t z80_in(void * param, uint16_t address) |
228 | { |
||
229 | struct z80_context * z80 = param; |
||
2241 | lvd | 230 | |
2258 | lvd | 231 | return (address&1) ? 0xFF : 0xBF; |
2254 | lvd | 232 | } |
2241 | lvd | 233 | |
2254 | lvd | 234 | |
235 | |||
236 | struct z80_context * z80_init(char * filename, int sys_type) |
||
2241 | lvd | 237 | { |
238 | // allocate structure for z80 context and associated data |
||
239 | // |
||
240 | |||
241 | struct z80_context * z80 = malloc(sizeof(struct z80_context)); |
||
242 | // |
||
243 | if( !z80 ) |
||
244 | { |
||
245 | fprintf(stderr,"%s: %d, %s: can't allocate memory for struct z80_context!\n",__FILE__,__LINE__,__func__); |
||
246 | exit(1); |
||
247 | } |
||
248 | |||
249 | // clear Z80 memory |
||
250 | memset(z80->z80_mem,0,65536); |
||
251 | |||
2242 | lvd | 252 | // load Z80 .com binary |
2241 | lvd | 253 | if( filename ) |
254 | { |
||
2254 | lvd | 255 | size_t load_address = (sys_type==SYS_ZX) ? 0x8000 : 0x0100; |
256 | |||
257 | |||
2241 | lvd | 258 | FILE * f = fopen(filename,"rb"); |
259 | if( !f ) |
||
260 | { |
||
261 | fprintf(stderr,"%s: %d, %s: can't open Z80 binary file <%s>!\n",__FILE__,__LINE__,__FUNCTION__,filename); |
||
262 | exit(1); |
||
263 | } |
||
264 | // |
||
2254 | lvd | 265 | size_t read=fread(z80->z80_mem+load_address,1,65536-load_address,f); |
2241 | lvd | 266 | off_t o=ftello(f); |
267 | int seek=fseeko(f,0,SEEK_END); |
||
268 | off_t e=ftello(f); |
||
2254 | lvd | 269 | if( seek || o!=e || read!=e || !(0<o && o<=(65536-load_address)) ) |
2241 | lvd | 270 | { |
2242 | lvd | 271 | fprintf(stderr,"%s: %d, %s: can't read Z80 .com file <%s>!\n",__FILE__,__LINE__,__FUNCTION__,filename); |
2241 | lvd | 272 | exit(1); |
273 | } |
||
274 | fclose(f); |
||
275 | } |
||
276 | |||
2254 | lvd | 277 | if( sys_type==SYS_ZX ) // load ROM |
278 | { |
||
279 | FILE * f = fopen("1982.rom","rb"); |
||
280 | if( !f ) |
||
281 | { |
||
282 | fprintf(stderr,"%s: %d, %s: can't open <1982.rom>!\n",__FILE__,__LINE__,__FUNCTION__); |
||
283 | exit(1); |
||
284 | } |
||
285 | // |
||
286 | size_t read=fread(z80->z80_mem,1,16384,f); |
||
287 | off_t o=ftello(f); |
||
288 | int seek=fseeko(f,0,SEEK_END); |
||
289 | off_t e=ftello(f); |
||
290 | if( seek || o!=e || read!=e || o!=16384 ) |
||
291 | { |
||
292 | fprintf(stderr,"%s: %d, %s: can't read <1982.rom>!\n",__FILE__,__LINE__,__FUNCTION__); |
||
293 | exit(1); |
||
294 | } |
||
295 | fclose(f); |
||
296 | } |
||
297 | |||
2258 | lvd | 298 | z80->z80.i = 0x3F; |
299 | z80->z80.af = 0x3222; |
||
300 | |||
2243 | lvd | 301 | // init cp/m stack value |
2254 | lvd | 302 | if( sys_type==SYS_CPM ) |
2243 | lvd | 303 | { |
304 | z80->z80_mem[6] = 0x00; |
||
305 | z80->z80_mem[7] = 0x40; |
||
306 | } |
||
2246 | lvd | 307 | |
2254 | lvd | 308 | // start address |
309 | z80->start_address = (sys_type==SYS_ZX) ? 0x8000 : 0x0100; |
||
310 | |||
311 | // start SP |
||
2258 | lvd | 312 | //z80->start_sp = (sys_type==SYS_ZX) ? 0x7FE8 : 0x4000; |
313 | z80->start_sp = (sys_type==SYS_ZX) ? 0xFFFD : 0x4000; |
||
314 | // return address (for ZX) |
||
315 | /* if( sys_type==SYS_ZX ) |
||
316 | { |
||
317 | z80->z80_mem[0x7FE8] = 0; |
||
318 | z80->z80_mem[0x7FE9] = 0; |
||
319 | } |
||
320 | */ |
||
2246 | lvd | 321 | // init callbacks |
2241 | lvd | 322 | z80->z80.context = (void *)z80; |
323 | |||
324 | z80->z80.nmia = NULL; |
||
325 | z80->z80.inta = NULL; |
||
326 | z80->z80.int_fetch = NULL; |
||
327 | z80->z80.ld_i_a = NULL; |
||
328 | z80->z80.ld_r_a = NULL; |
||
329 | z80->z80.reti = NULL; |
||
330 | z80->z80.retn = NULL; |
||
331 | z80->z80.illegal = NULL; |
||
332 | |||
2254 | lvd | 333 | z80->z80.fetch_opcode = (sys_type==SYS_ZX ) ? (&z80_filter_zx_api) : |
334 | (sys_type==SYS_NEDOOS) ? (&z80_filter_nedoos_api) : |
||
335 | (sys_type==SYS_CPM ) ? (&z80_filter_cpm_api) : NULL; |
||
2243 | lvd | 336 | |
2241 | lvd | 337 | z80->z80.fetch = &z80_rd; |
338 | z80->z80.read = &z80_rd; |
||
339 | z80->z80.nop = &z80_rd; |
||
340 | |||
2254 | lvd | 341 | z80->z80.write = (sys_type==SYS_ZX) ? &z80_zx_wr : &z80_wr; |
2241 | lvd | 342 | |
343 | z80->z80.hook = NULL; |
||
344 | |||
2254 | lvd | 345 | z80->z80.in = &z80_in; |
346 | z80->z80.out = &z80_out; |
||
2241 | lvd | 347 | |
348 | z80->z80.halt = &z80_hlt; |
||
349 | |||
350 | |||
2258 | lvd | 351 | z80->z80.options = Z80_MODEL_ZILOG_NMOS; |
352 | |||
353 | |||
2241 | lvd | 354 | return z80; |
355 | } |
||
356 | |||
357 | |||
358 | |||
359 | |||
2254 | lvd | 360 | size_t z80_exec(struct z80_context * z80, size_t max_clocks) |
2241 | lvd | 361 | { |
2247 | lvd | 362 | z80->was_ed = 0; |
363 | |||
2241 | lvd | 364 | z80_power(&z80->z80,1); |
365 | |||
2254 | lvd | 366 | z80->z80.pc = z80->start_address; |
367 | z80->z80.sp = z80->start_sp; |
||
2241 | lvd | 368 | |
369 | size_t clocks = z80_execute(&z80->z80, max_clocks); |
||
370 | |||
371 | |||
372 | if( !z80->z80.halt_line ) |
||
373 | return 0; |
||
374 | |||
375 | return clocks; |
||
376 | } |
||
377 | |||
378 | |||
379 | |||
380 | |||
381 | |||
382 | uint8_t z80_rdbyte(struct z80_context * z80, uint16_t addr) |
||
383 | { |
||
384 | return z80->z80_mem[addr]; |
||
385 | } |
||
386 | |||
387 | uint16_t z80_rdword_le(struct z80_context * z80, uint16_t addr) |
||
388 | { |
||
389 | return (((uint16_t)z80_rdbyte(z80,addr+0)) & 0x00FF) | |
||
390 | (((uint16_t)z80_rdbyte(z80,addr+1)) << 8 ) ; |
||
391 | } |
||
392 | |||
393 | uint32_t z80_rdlong_le(struct z80_context * z80, uint16_t addr) |
||
394 | { |
||
395 | return (((uint32_t)z80_rdword_le(z80,addr+0)) & 0x0000FFFF) | |
||
396 | (((uint32_t)z80_rdword_le(z80,addr+2)) << 16 ) ; |
||
397 | } |
||
398 | |||
399 | uint64_t z80_rdocta_le(struct z80_context * z80, uint16_t addr) |
||
400 | { |
||
401 | return (((uint64_t)z80_rdlong_le(z80,addr+0)) & 0xFFFFFFFFull) | |
||
402 | (((uint64_t)z80_rdlong_le(z80,addr+4)) << 32 ) ; |
||
403 | } |
||
404 | |||
405 | void z80_wrbyte(struct z80_context * z80, uint16_t addr, uint8_t data) |
||
406 | { |
||
407 | z80->z80_mem[addr]=data; |
||
408 | } |
||
409 | |||
410 | void z80_wrword_le(struct z80_context * z80, uint16_t addr, uint16_t data) |
||
411 | { |
||
412 | z80_wrbyte(z80,addr+0,data & 0x00FF); |
||
413 | z80_wrbyte(z80,addr+1,data >> 8 ); |
||
414 | } |
||
415 | |||
416 | void z80_wrlong_le(struct z80_context * z80, uint16_t addr, uint32_t data) |
||
417 | { |
||
418 | z80_wrword_le(z80,addr+0,data & 0x0000FFFF); |
||
419 | z80_wrword_le(z80,addr+2,data >> 16 ); |
||
420 | } |
||
421 | |||
422 | void z80_wrocta_le(struct z80_context * z80, uint16_t addr, uint64_t data) |
||
423 | { |
||
424 | z80_wrlong_le(z80,addr+0,data & 0xFFFFFFFFull); |
||
425 | z80_wrlong_le(z80,addr+4,data >> 32 ); |
||
426 | } |
||
427 |