1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <signal.h>
23 #include <unistd.h>
24 #include <iostream>
25 #include <iomanip>
26 #include <sys/time.h>
27
28 #include <z80ex/z80ex_dasm.h>
29
30 #include "kc/system.h"
31 #include "kc/prefs/prefs.h"
32
33 #include "kc/z80.h"
34 #include "kc/daisy.h"
35 #include "kc/timer.h"
36 #include "kc/ports.h"
37 #include "kc/memory.h"
38 #include "kc/z80_fdc.h"
39 #include "kc/cb_list.h"
40
41 #include "sys/sysdep.h"
42
43 #include "cmd/cmd.h"
44
45 #include "ui/ui.h"
46
47 #include "libdbg/dbg.h"
48
49 using namespace std;
50
51 static Z80 *self; // for the signal handler
52 static void signalHandler(int sig);
53
54 #if 0
55 unsigned long long rdtsc(void)
56 {
57 union {
58 struct {
59 unsigned long lo;
60 unsigned long hi;
61 } l;
62 unsigned long long ll;
63 } ret;
64
65 __asm__ volatile ("rdtsc" : "=a" (ret.l.lo), "=d" (ret.l.hi));
66
67 return ret.ll;
68 }
69 #endif
70
71 class CMD_single_step : public CMD
72 {
73 private:
74 Z80 *_z80;
75 public:
CMD_single_step(Z80 * z80)76 CMD_single_step(Z80 *z80) : CMD("z80-single-step")
77 {
78 _z80 = z80;
79 register_cmd("z80-single-step-on", 0);
80 register_cmd("z80-single-step-off", 1);
81 register_cmd("z80-single-step-toggle", 2);
82 register_cmd("z80-execute-step", 3);
83 register_cmd("z80-trace-on", 4);
84 register_cmd("z80-trace-off", 5);
85 register_cmd("z80-trace-toggle", 6);
86 register_cmd("z80-trace-set-delay", 7);
87 }
88
execute(CMD_Args * args,CMD_Context context)89 void execute(CMD_Args *args, CMD_Context context)
90 {
91 switch (context)
92 {
93 case 0:
94 z80->singlestep(true);
95 break;
96 case 1:
97 z80->singlestep(false);
98 break;
99 case 2:
100 z80->singlestep(!z80->singlestep());
101 break;
102 case 3:
103 z80->executestep();
104 break;
105 case 4:
106 z80->trace(true);
107 break;
108 case 5:
109 z80->trace(false);
110 break;
111 case 6:
112 z80->trace(!z80->trace());
113 break;
114 case 7:
115 if (args)
116 {
117 long delay = 1000 * args->get_long_arg("delay");
118 z80->tracedelay(delay);
119 }
120 break;
121 }
122 }
123 };
124
Z80(void)125 Z80::Z80(void)
126 {
127 self = this;
128
129 _context = z80ex_create(z80ex_mread_cb, this, z80ex_mwrite_cb, this,
130 z80ex_pread_cb, this, z80ex_pwrite_cb, this,
131 z80ex_intread_cb, this);
132
133 z80ex_set_reti_callback(_context, z80ex_reti_cb, this);
134
135 z80ex_reset(_context);
136
137 const EmulationType &emulation_type = Preferences::instance()->get_system_type()->get_emulation_type();
138 z80ex_set_reg(_context, regPC, emulation_type.get_power_on_addr());
139
140 /*
141 * FIXME: at least z1013 emulation breaks with the stackpointer
142 * FIXME: initialized with 0xf000; the CP/M bootlader BL4 will
143 * FIXME: overwrite it's own stack when clearing the screen :-(
144 */
145 z80ex_set_reg(_context, regSP, 0x0000);
146
147 _counter = 0;
148
149 _debug = false;
150 _trace = false;
151 _singlestep = false;
152 _executestep = false;
153 _enable_floppy_cpu = false;
154
155 _tracedelay = 100000;
156
157 _irq_line = 0;
158 _irq_mask = 1;
159
160 _do_quit = false;
161 }
162
163 #if 0
164 static void
165 print_regs(_Z80 *r)
166 {
167 int c1, c2;
168
169 z80->printPC();
170
171 cout << "a=" << setw(2) << setfill('0') << hex << r->AF.B.l
172 << ", bc=" << setw(4) << setfill('0') << hex << r->BC.W
173 << ", de=" << setw(4) << setfill('0') << hex << r->DE.W
174 << ", hl=" << setw(4) << setfill('0') << hex << r->HL.W
175 << endl;
176
177 c1 = RdZ80(r->DE.W) & 0xff;
178 c2 = RdZ80(r->HL.W) & 0xff;
179
180 cout << "(de)=" << setw(2) << setfill('0') << hex << c1
181 << " '" << (isprint(c1) ? c1 : '.') << "' "
182 << "(hl)=" << setw(2) << setfill('0') << hex << c2
183 << " '" << (isprint(c2) ? c2 : '.') << "'"
184 << endl;
185 }
186 #endif
187
188 void
executestep(void)189 Z80::executestep(void)
190 {
191 _executestep = true;
192 }
193
194 void
singlestep(bool value)195 Z80::singlestep(bool value)
196 {
197 _singlestep = value;
198 }
199
200 bool
singlestep()201 Z80::singlestep()
202 {
203 return _singlestep;
204 }
205
206 bool
run(void)207 Z80::run(void)
208 {
209 int a;
210 CMD *cmd;
211
212 signal(SIGINT, signalHandler);
213
214 if (timer)
215 timer->start();
216
217 cmd = new CMD_single_step(this);
218
219 a = 0;
220 while (!_do_quit)
221 {
222 // if (DBG_check("KCemu/Z80core/trace"))
223 // debug(true);
224
225 //if (_regs.PC.W <= 0x8000)
226 //z80->printPC(); cout << endl;
227
228 //if (_regs.PC.W == 0x0170)
229 //z80->debug(true);
230
231 if (_singlestep)
232 {
233 ui->update(true);
234 if (!_executestep)
235 {
236 sys_usleep(100000);
237 continue;
238 }
239 CMD_EXEC("single-step-executed");
240 _executestep = false;
241
242 }
243 else
244 if (_trace)
245 {
246 ui->update();
247 CMD_EXEC("single-step-executed");
248 sys_usleep(_tracedelay);
249 }
250
251 if (_debug)
252 {
253 int addr = getPC();
254 char buf[80];
255 int t, t2;
256 int base_addr;
257
258 printf("%04X: ", addr);
259 addr += z80ex_dasm(buf, 80, 0, &t, &t2, z80ex_dasm_readbyte_cb, addr, &base_addr);
260 printf("%-15s t=%d", buf, t);
261 if (t2) printf("/%d", t2);
262 printf("\n");
263 }
264
265 int tstates = z80ex_step(_context);
266
267 if (_irq_line && z80ex_int_possible(_context))
268 {
269 word_t irq = daisy->irq_ack();
270 if (irq != IRQ_NOT_ACK)
271 {
272 _next_irq = (byte_t)irq;
273 z80ex_int(_context);
274 }
275 }
276
277 if (_enable_floppy_cpu && fdc_z80)
278 fdc_z80->execute();
279
280 _counter += tstates;
281
282 _cb_list.run_callbacks(_counter);
283 }
284
285 return false;
286 }
287
288 void
quit(void)289 Z80::quit(void)
290 {
291 _do_quit = true;
292 }
293
294 void
addCallback(unsigned long long offset,Callback * cb,void * data)295 Z80::addCallback(unsigned long long offset, Callback *cb, void *data)
296 {
297 _cb_list.add_callback(getCounter() + offset, cb, data);
298 }
299
300 void
remove_callback_listener(Callback * cb)301 Z80::remove_callback_listener(Callback *cb)
302 {
303 _cb_list.remove_callback_listener(cb);
304 }
305
306 bool
debug(void)307 Z80::debug(void)
308 {
309 return _debug;
310 }
311
312 void
debug(bool value)313 Z80::debug(bool value)
314 {
315 _debug = value;
316 if (_enable_floppy_cpu && fdc_z80)
317 fdc_z80->trace(value);
318 }
319
320 bool
trace(void)321 Z80::trace(void)
322 {
323 return _trace;
324 //return _regs.Trace;
325 }
326
327 void
trace(bool value)328 Z80::trace(bool value)
329 {
330 _trace = value;
331 //if (level < 0) level = 0;
332 //_regs.Trace = level;
333 }
334
335 void
tracedelay(long delay)336 Z80::tracedelay(long delay)
337 {
338 _tracedelay = delay;
339 }
340
341 void
reset(word_t pc,bool power_on)342 Z80::reset(word_t pc, bool power_on)
343 {
344 _cb_list.clear();
345
346 for (ic_list_t::iterator it = _ic_list.begin();it != _ic_list.end();it++)
347 (*it)->reset(power_on);
348
349 module->reset(power_on);
350
351 z80ex_reset(_context);
352 z80ex_set_reg(_context, regPC, pc);
353
354 /*
355 * FIXME: at least z1013 emulation breaks with the stackpointer
356 * FIXME: initialized with 0xf000; the CP/M bootlader BL4 will
357 * FIXME: overwrite it's own stack when clearing the screen :-(
358 */
359 z80ex_set_reg(_context, regSP, 0x0000);
360
361 halt_floppy_cpu(power_on);
362
363 if (timer)
364 timer->start();
365 }
366
367 void
reset(void)368 Z80::reset(void)
369 {
370 const EmulationType &emulation_type = Preferences::instance()->get_system_type()->get_emulation_type();
371 reset(emulation_type.get_reset_addr(), false);
372 }
373
374 void
power_on(void)375 Z80::power_on(void)
376 {
377 const EmulationType &emulation_type = Preferences::instance()->get_system_type()->get_emulation_type();
378 reset(emulation_type.get_power_on_addr(), true);
379 }
380
381 void
jump(word_t pc)382 Z80::jump(word_t pc)
383 {
384 z80ex_set_reg(_context, regPC, pc);
385 }
386
387 void
register_ic(InterfaceCircuit * h)388 Z80::register_ic(InterfaceCircuit *h)
389 {
390 _ic_list.push_back(h);
391 }
392
393 void
unregister_ic(InterfaceCircuit * h)394 Z80::unregister_ic(InterfaceCircuit *h)
395 {
396 _ic_list.remove(h);
397 }
398
399 dword_t
get_irq_mask(void)400 Z80::get_irq_mask(void)
401 {
402 dword_t val = _irq_mask;
403
404 if (val == 0)
405 {
406 DBG(0, form("KCemu/warning",
407 "get_irq_mask(): too many interrupt sources!\n"));
408 }
409
410 _irq_mask <<= 1;
411
412 return val;
413 }
414
415 void
set_irq_line(dword_t mask)416 Z80::set_irq_line(dword_t mask)
417 {
418 dword_t irq_line = _irq_line | mask;
419 DBG(2, form("KCemu/Z80/irq",
420 "set_irq_line(): %04x: %04x -> %04x\n",
421 mask, _irq_line, irq_line));
422 _irq_line = irq_line;
423 }
424
425 void
reset_irq_line(dword_t mask)426 Z80::reset_irq_line(dword_t mask)
427 {
428 _irq_line = _irq_line & (~mask);
429 DBG(2, form("KCemu/Z80/irq",
430 "reset_irq_line(): %04x -> %04x\n",
431 mask, _irq_line));
432 }
433
434 void
reti(void)435 Z80::reti(void)
436 {
437 daisy->reti();
438 }
439
440 void
nmi(void)441 Z80::nmi(void)
442 {
443 z80ex_nmi(_context);
444 }
445
446 void
printPC(void)447 Z80::printPC(void)
448 {
449 cout << setw(4) << setfill('0') << hex << getPC() << "h: ";
450 }
451
452 void
start_floppy_cpu(void)453 Z80::start_floppy_cpu(void)
454 {
455 if (fdc_z80)
456 {
457 fdc_z80->reset();
458 _enable_floppy_cpu = true;
459 }
460 }
461
462 void
halt_floppy_cpu(bool power_on)463 Z80::halt_floppy_cpu(bool power_on)
464 {
465 if (fdc_z80)
466 {
467 fdc_z80->reset(power_on);
468 _enable_floppy_cpu = false;
469 }
470 }
471
472 Z80EX_BYTE
z80ex_dasm_readbyte_cb(Z80EX_WORD addr,void * user_data)473 Z80::z80ex_dasm_readbyte_cb(Z80EX_WORD addr, void *user_data)
474 {
475 return memory->memRead8(addr);
476 }
477
478 Z80EX_BYTE
z80ex_mread_cb(Z80EX_CONTEXT * cpu,Z80EX_WORD addr,int m1_state,void * user_data)479 Z80::z80ex_mread_cb(Z80EX_CONTEXT *cpu, Z80EX_WORD addr, int m1_state, void *user_data)
480 {
481 return memory->memRead8(addr);
482 }
483
484 void
z80ex_mwrite_cb(Z80EX_CONTEXT * cpu,Z80EX_WORD addr,Z80EX_BYTE value,void * user_data)485 Z80::z80ex_mwrite_cb(Z80EX_CONTEXT *cpu, Z80EX_WORD addr, Z80EX_BYTE value, void *user_data)
486 {
487 memory->memWrite8(addr, value);
488 }
489
490 Z80EX_BYTE
z80ex_pread_cb(Z80EX_CONTEXT * cpu,Z80EX_WORD port,void * user_data)491 Z80::z80ex_pread_cb(Z80EX_CONTEXT *cpu, Z80EX_WORD port, void *user_data)
492 {
493 byte_t value;
494
495 value = ports->in(port);
496 DBG(2, form("KCemu/Z80/pread_cb",
497 "Z80::z80ex_pread_cb(): %04x -> %02x\n",
498 port, value));
499 return value;
500 }
501
502 void
z80ex_pwrite_cb(Z80EX_CONTEXT * cpu,Z80EX_WORD port,Z80EX_BYTE value,void * user_data)503 Z80::z80ex_pwrite_cb(Z80EX_CONTEXT *cpu, Z80EX_WORD port, Z80EX_BYTE value, void *user_data)
504 {
505 DBG(2, form("KCemu/Z80/pwrite_cb",
506 "Z80::z80ex_pwrite_cb(): %04x -> %02x\n",
507 port, value));
508 ports->out(port, value);
509 }
510
511 Z80EX_BYTE
z80ex_intread_cb(Z80EX_CONTEXT * cpu,void * user_data)512 Z80::z80ex_intread_cb(Z80EX_CONTEXT *cpu, void *user_data)
513 {
514 Z80 *z80 = (Z80 *)user_data;
515 DBG(2, form("KCemu/Z80/intread_cb",
516 "Z80::z80ex_intread_cb(): %02x\n",
517 z80->_next_irq));
518 return z80->_next_irq;
519 }
520
521 void
z80ex_reti_cb(Z80EX_CONTEXT * cpu,void * user_data)522 Z80::z80ex_reti_cb(Z80EX_CONTEXT *cpu, void *user_data)
523 {
524 Z80 *z80 = (Z80 *)user_data;
525 DBG(2, form("KCemu/Z80/reti_cb",
526 "Z80::z80ex_reti_cb(): RETI\n"));
527 z80->reti();
528 }
529
530 static void
signalHandler(int sig)531 signalHandler(int sig)
532 {
533 static bool flag = false;
534 cout << "\n *** signal caught (" << sig << ") ***\n\n";
535 signal(sig, signalHandler);
536 flag = !flag;
537 self->debug(flag);
538 }
539