1 /*
2 * libtilemcore - Graphing calculator emulation library
3 *
4 * Copyright (C) 2009-2011 Benjamin Moody
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include "tilem.h"
28 #include "z80.h"
29
30 /* Timer manipulation */
31
32 /*
33 static void dumptimers(TilemZ80* z80)
34 {
35 int tmr;
36 int t;
37
38 printf("*** RT:");
39 for (tmr = z80->timer_rt; tmr; tmr = z80->timers[tmr].next) {
40 t = z80->timers[tmr].count - z80->clock;
41 printf(" %d:%d", tmr, t);
42 }
43 printf("\n*** CPU:");
44 for (tmr = z80->timer_cpu; tmr; tmr = z80->timers[tmr].next) {
45 t = z80->timers[tmr].count - z80->clock;
46 printf(" %d:%d", tmr, t);
47 }
48 printf("\n*** Free:");
49 for (tmr = z80->timer_free; tmr; tmr = z80->timers[tmr].next) {
50 printf(" %d", tmr);
51 }
52 printf("\n");
53 }
54 */
55
timer_free(TilemZ80 * z80,int tmr)56 static inline void timer_free(TilemZ80* z80, int tmr)
57 {
58 z80->timers[tmr].callback = NULL;
59 z80->timers[tmr].callbackdata = NULL;
60 z80->timers[tmr].next = z80->timer_free;
61 z80->timers[tmr].prev = 0;
62 z80->timer_free = tmr;
63 }
64
timer_alloc(TilemZ80 * z80)65 static inline int timer_alloc(TilemZ80* z80)
66 {
67 int tmr, i;
68
69 if (z80->timer_free) {
70 tmr = z80->timer_free;
71 z80->timer_free = z80->timers[tmr].next;
72 z80->timers[tmr].next = 0;
73 return tmr;
74 }
75
76 i = z80->ntimers;
77 z80->ntimers = i * 2 + 1;
78 z80->timers = tilem_renew(TilemZ80Timer, z80->timers, z80->ntimers);
79 while (i < z80->ntimers) {
80 timer_free(z80, i);
81 i++;
82 }
83
84 tmr = z80->timer_free;
85 z80->timer_free = z80->timers[tmr].next;
86 z80->timers[tmr].next = 0;
87 return tmr;
88 }
89
timer_earlier(TilemZ80 * z80,int tmr1,int tmr2)90 static inline int timer_earlier(TilemZ80* z80, int tmr1, int tmr2)
91 {
92 dword count1, count2;
93
94 count1 = z80->timers[tmr1].count + 10000 - z80->clock;
95 count2 = z80->timers[tmr2].count + 10000 - z80->clock;
96
97 return (count1 < count2);
98 }
99
timer_insert(TilemZ80 * z80,int * list,int tmr)100 static inline void timer_insert(TilemZ80* z80, int* list, int tmr)
101 {
102 int prev, next;
103
104 if (!*list || timer_earlier(z80, tmr, *list)) {
105 z80->timers[tmr].prev = 0;
106 z80->timers[tmr].next = *list;
107 z80->timers[*list].prev = tmr;
108 *list = tmr;
109 return;
110 }
111
112 prev = *list;
113 next = z80->timers[prev].next;
114
115 while (next && timer_earlier(z80, next, tmr)) {
116 prev = next;
117 next = z80->timers[prev].next;
118 }
119
120 z80->timers[prev].next = tmr;
121 z80->timers[next].prev = tmr;
122 z80->timers[tmr].prev = prev;
123 z80->timers[tmr].next = next;
124 }
125
timer_set(TilemZ80 * z80,int tmr,dword count,dword period,int rt,dword extra)126 static inline void timer_set(TilemZ80* z80, int tmr, dword count,
127 dword period, int rt, dword extra)
128 {
129 dword clocks;
130 qword kclocks;
131
132 if (!count) {
133 /* leave timer disabled */
134 z80->timers[tmr].prev = 0;
135 z80->timers[tmr].next = 0;
136 }
137 else if (rt) {
138 kclocks = z80->clockspeed;
139 kclocks *= count;
140 clocks = (kclocks + 500) / 1000 - extra;
141 z80->timers[tmr].count = z80->clock + clocks;
142 z80->timers[tmr].period = period;
143 timer_insert(z80, &z80->timer_rt, tmr);
144 }
145 else {
146 clocks = count - extra;
147 z80->timers[tmr].count = z80->clock + clocks;
148 z80->timers[tmr].period = period;
149 timer_insert(z80, &z80->timer_cpu, tmr);
150 }
151 }
152
timer_unset(TilemZ80 * z80,int tmr)153 static inline void timer_unset(TilemZ80* z80, int tmr)
154 {
155 int prev, next;
156
157 if (tmr == z80->timer_cpu)
158 z80->timer_cpu = z80->timers[tmr].next;
159 if (tmr == z80->timer_rt)
160 z80->timer_rt = z80->timers[tmr].next;
161
162 prev = z80->timers[tmr].prev;
163 next = z80->timers[tmr].next;
164 z80->timers[prev].next = next;
165 z80->timers[next].prev = prev;
166 z80->timers[tmr].prev = 0;
167 z80->timers[tmr].next = 0;
168 }
169
170
171 /* Breakpoint manipulation */
172
bp_free(TilemZ80 * z80,int bp)173 static inline void bp_free(TilemZ80* z80, int bp)
174 {
175 z80->breakpoints[bp].type = 0;
176 z80->breakpoints[bp].testfunc = NULL;
177 z80->breakpoints[bp].testdata = NULL;
178 z80->breakpoints[bp].next = z80->breakpoint_free;
179 z80->breakpoints[bp].prev = 0;
180 z80->breakpoint_free = bp;
181 }
182
bp_alloc(TilemZ80 * z80)183 static inline int bp_alloc(TilemZ80* z80)
184 {
185 int bp, i;
186
187 if (z80->breakpoint_free) {
188 bp = z80->breakpoint_free;
189 z80->breakpoint_free = z80->breakpoints[bp].next;
190 return bp;
191 }
192
193 i = z80->nbreakpoints;
194 z80->nbreakpoints = i * 2 + 2;
195 z80->breakpoints = tilem_renew(TilemZ80Breakpoint, z80->breakpoints,
196 z80->nbreakpoints);
197 while (i < z80->nbreakpoints) {
198 bp_free(z80, i);
199 i++;
200 }
201
202 bp = z80->breakpoint_free;
203 z80->breakpoint_free = z80->breakpoints[bp].next;
204 return bp;
205 }
206
bp_head(TilemCalc * calc,int type)207 static int* bp_head(TilemCalc *calc, int type)
208 {
209 if (type & TILEM_BREAK_DISABLED)
210 return &calc->z80.breakpoint_disabled;
211
212 switch (type & TILEM_BREAK_TYPE_MASK) {
213 case TILEM_BREAK_MEM_READ:
214 return (type & TILEM_BREAK_PHYSICAL
215 ? &calc->z80.breakpoint_mpr
216 : &calc->z80.breakpoint_mr);
217
218 case TILEM_BREAK_MEM_EXEC:
219 return (type & TILEM_BREAK_PHYSICAL
220 ? &calc->z80.breakpoint_mpx
221 : &calc->z80.breakpoint_mx);
222
223 case TILEM_BREAK_MEM_WRITE:
224 return (type & TILEM_BREAK_PHYSICAL
225 ? &calc->z80.breakpoint_mpw
226 : &calc->z80.breakpoint_mw);
227
228 case TILEM_BREAK_PORT_READ:
229 return &calc->z80.breakpoint_pr;
230
231 case TILEM_BREAK_PORT_WRITE:
232 return &calc->z80.breakpoint_pw;
233
234 case TILEM_BREAK_EXECUTE:
235 return &calc->z80.breakpoint_op;
236
237 default:
238 tilem_internal(calc, "invalid bp type");
239 return 0;
240 }
241 }
242
bp_add(TilemCalc * calc,int bp,int type)243 static int bp_add(TilemCalc *calc, int bp, int type)
244 {
245 int *head = bp_head(calc, type);
246
247 if (!head) {
248 bp_free(&calc->z80, bp);
249 return 0;
250 }
251
252 calc->z80.breakpoints[bp].next = *head;
253 calc->z80.breakpoints[*head].prev = *head ? bp : 0;
254 *head = bp;
255
256 return bp;
257 }
258
bp_rem(TilemCalc * calc,int bp,int type)259 static void bp_rem(TilemCalc *calc, int bp, int type)
260 {
261 int prev, next;
262 int *head = bp_head(calc, type);
263
264 prev = calc->z80.breakpoints[bp].prev;
265 next = calc->z80.breakpoints[bp].next;
266
267 if (bp == *head)
268 *head = next;
269
270 calc->z80.breakpoints[prev].next = prev ? next : 0;
271 calc->z80.breakpoints[next].prev = next ? prev : 0;
272 }
273
invoke_ptimer(TilemCalc * calc,void * data)274 static void invoke_ptimer(TilemCalc* calc, void* data)
275 {
276 (*calc->hw.z80_ptimer)(calc, TILEM_PTR_TO_DWORD(data));
277 }
278
279 /* Z80 API */
280
tilem_z80_reset(TilemCalc * calc)281 void tilem_z80_reset(TilemCalc* calc)
282 {
283 int i;
284
285 AF = BC = DE = HL = AF2 = BC2 = DE2 = HL2 = 0xffff;
286 IX = IY = IR = SP = WZ = WZ2 = 0xffff;
287 PC = 0;
288 Rh = 0x80;
289 IFF1 = IFF2 = IM = 0;
290 calc->z80.interrupts = 0;
291 calc->z80.halted = 0;
292
293 /* Set up hardware timers */
294 if (!calc->z80.ntimers) {
295 calc->z80.ntimers = (calc->hw.nhwtimers
296 + TILEM_NUM_SYS_TIMERS + 1);
297 tilem_free(calc->z80.timers);
298 calc->z80.timers = tilem_new(TilemZ80Timer, calc->z80.ntimers);
299
300 for (i = 1; i < calc->z80.ntimers; i++) {
301 calc->z80.timers[i].next = 0;
302 calc->z80.timers[i].prev = 0;
303 calc->z80.timers[i].count = 0;
304 calc->z80.timers[i].period = 0;
305 calc->z80.timers[i].callback = &invoke_ptimer;
306 calc->z80.timers[i].callbackdata = TILEM_DWORD_TO_PTR(i);
307 }
308
309 calc->z80.timers[TILEM_TIMER_LCD_DELAY].callback
310 = &tilem_lcd_delay_timer;
311 calc->z80.timers[TILEM_TIMER_FLASH_DELAY].callback
312 = tilem_flash_delay_timer;
313 calc->z80.timers[TILEM_TIMER_LINK_ASSIST].callback
314 = tilem_linkport_assist_timer;
315
316 for (i = 0; i < TILEM_MAX_USER_TIMERS; i++) {
317 calc->z80.timers[TILEM_TIMER_USER1 + i].callback
318 = tilem_user_timer_expired;
319 calc->z80.timers[TILEM_TIMER_USER1 + i].callbackdata
320 = TILEM_DWORD_TO_PTR(i);
321 }
322 }
323 }
324
tilem_z80_stop(TilemCalc * calc,dword reason)325 void tilem_z80_stop(TilemCalc* calc, dword reason)
326 {
327 if (!(reason & calc->z80.stop_mask)) {
328 calc->z80.stop_reason |= reason;
329 calc->z80.stopping = 1;
330 }
331 }
332
tilem_z80_exception(TilemCalc * calc,unsigned type)333 void tilem_z80_exception(TilemCalc* calc, unsigned type)
334 {
335 calc->z80.exception |= type;
336 }
337
tilem_z80_set_speed(TilemCalc * calc,int speed)338 void tilem_z80_set_speed(TilemCalc* calc, int speed)
339 {
340 int tmr;
341 qword t;
342 int oldspeed = calc->z80.clockspeed;
343
344 if (oldspeed == speed)
345 return;
346
347 for (tmr = calc->z80.timer_rt; tmr; tmr = calc->z80.timers[tmr].next) {
348 if ((calc->z80.clock - calc->z80.timers[tmr].count) < 10000)
349 continue;
350
351 t = calc->z80.timers[tmr].count - calc->z80.clock;
352 t = (t * speed + oldspeed / 2) / oldspeed;
353 calc->z80.timers[tmr].count = calc->z80.clock + t;
354 }
355
356 calc->z80.clockspeed = speed;
357 }
358
tilem_z80_add_timer(TilemCalc * calc,dword count,dword period,int rt,TilemZ80TimerFunc func,void * data)359 int tilem_z80_add_timer(TilemCalc* calc, dword count, dword period,
360 int rt, TilemZ80TimerFunc func, void* data)
361 {
362 int id;
363
364 id = timer_alloc(&calc->z80);
365 calc->z80.timers[id].callback = func;
366 calc->z80.timers[id].callbackdata = data;
367 timer_set(&calc->z80, id, count, period, rt, 0);
368 return id;
369 }
370
tilem_z80_set_timer(TilemCalc * calc,int id,dword count,dword period,int rt)371 void tilem_z80_set_timer(TilemCalc* calc, int id, dword count,
372 dword period, int rt)
373 {
374 if (id < 1 || id > calc->z80.ntimers
375 || !calc->z80.timers[id].callback) {
376 tilem_internal(calc, "setting invalid timer %d", id);
377 return;
378 }
379 timer_unset(&calc->z80, id);
380 timer_set(&calc->z80, id, count, period, rt, 0);
381 }
382
tilem_z80_set_timer_period(TilemCalc * calc,int id,dword period)383 void tilem_z80_set_timer_period(TilemCalc* calc, int id, dword period)
384 {
385 if (id < 1 || id > calc->z80.ntimers
386 || !calc->z80.timers[id].callback) {
387 tilem_internal(calc, "setting invalid timer %d", id);
388 return;
389 }
390
391 calc->z80.timers[id].period = period;
392 }
393
tilem_z80_remove_timer(TilemCalc * calc,int id)394 void tilem_z80_remove_timer(TilemCalc* calc, int id)
395 {
396 if (id <= calc->hw.nhwtimers + TILEM_NUM_SYS_TIMERS
397 || id > calc->z80.ntimers || !calc->z80.timers[id].callback) {
398 tilem_internal(calc, "removing invalid timer %d", id);
399 return;
400 }
401 timer_unset(&calc->z80, id);
402 timer_free(&calc->z80, id);
403 }
404
tilem_z80_timer_running(TilemCalc * calc,int id)405 int tilem_z80_timer_running(TilemCalc* calc, int id)
406 {
407 if (id < 1 || id > calc->z80.ntimers
408 || !calc->z80.timers[id].callback) {
409 tilem_internal(calc, "querying invalid timer %d", id);
410 return 0;
411 }
412
413 if (id == calc->z80.timer_rt || id == calc->z80.timer_cpu)
414 return 1;
415 if (calc->z80.timers[id].prev)
416 return 1;
417 return 0;
418 }
419
tilem_z80_get_timer_clocks(TilemCalc * calc,int id)420 int tilem_z80_get_timer_clocks(TilemCalc* calc, int id)
421 {
422 if (id < 1 || id > calc->z80.ntimers
423 || !calc->z80.timers[id].callback) {
424 tilem_internal(calc, "querying invalid timer %d", id);
425 return 0;
426 }
427 return (calc->z80.timers[id].count - calc->z80.clock);
428 }
429
tilem_z80_get_timer_microseconds(TilemCalc * calc,int id)430 int tilem_z80_get_timer_microseconds(TilemCalc* calc, int id)
431 {
432 int n = tilem_z80_get_timer_clocks(calc, id);
433
434 if (n < 0) {
435 n = ((((qword) -n * 1000) + (calc->z80.clockspeed / 2))
436 / calc->z80.clockspeed);
437 return -n;
438 }
439 else {
440 n = ((((qword) n * 1000) + (calc->z80.clockspeed / 2))
441 / calc->z80.clockspeed);
442 return n;
443 }
444 }
445
tilem_z80_add_breakpoint(TilemCalc * calc,int type,dword start,dword end,dword mask,TilemZ80BreakpointFunc func,void * data)446 int tilem_z80_add_breakpoint(TilemCalc* calc, int type,
447 dword start, dword end, dword mask,
448 TilemZ80BreakpointFunc func,
449 void* data)
450 {
451 int bp;
452
453 bp = bp_alloc(&calc->z80);
454
455 calc->z80.breakpoints[bp].type = type;
456 calc->z80.breakpoints[bp].start = start;
457 calc->z80.breakpoints[bp].end = end;
458 calc->z80.breakpoints[bp].mask = mask;
459 calc->z80.breakpoints[bp].testfunc = func;
460 calc->z80.breakpoints[bp].testdata = data;
461 calc->z80.breakpoints[bp].prev = 0;
462
463 return bp_add(calc, bp, type);
464 }
465
tilem_z80_remove_breakpoint(TilemCalc * calc,int id)466 void tilem_z80_remove_breakpoint(TilemCalc* calc, int id)
467 {
468 if (id < 1 || id > calc->z80.nbreakpoints
469 || !calc->z80.breakpoints[id].type) {
470 tilem_internal(calc,
471 "attempt to remove invalid breakpoint %d", id);
472 return;
473 }
474
475 bp_rem(calc, id, calc->z80.breakpoints[id].type);
476
477 bp_free(&calc->z80, id);
478 }
479
tilem_z80_enable_breakpoint(TilemCalc * calc,int id)480 void tilem_z80_enable_breakpoint(TilemCalc* calc, int id)
481 {
482 int type = tilem_z80_get_breakpoint_type(calc, id);
483 tilem_z80_set_breakpoint_type(calc, id, type & ~TILEM_BREAK_DISABLED);
484 }
485
tilem_z80_disable_breakpoint(TilemCalc * calc,int id)486 void tilem_z80_disable_breakpoint(TilemCalc* calc, int id)
487 {
488 int type = tilem_z80_get_breakpoint_type(calc, id);
489 tilem_z80_set_breakpoint_type(calc, id, type | TILEM_BREAK_DISABLED);
490 }
491
tilem_z80_breakpoint_enabled(TilemCalc * calc,int id)492 int tilem_z80_breakpoint_enabled(TilemCalc* calc, int id)
493 {
494 int type = tilem_z80_get_breakpoint_type(calc, id);
495 return !(type & TILEM_BREAK_DISABLED);
496 }
497
tilem_z80_get_breakpoint_type(TilemCalc * calc,int id)498 int tilem_z80_get_breakpoint_type(TilemCalc* calc, int id)
499 {
500 if (id < 1 || id > calc->z80.nbreakpoints
501 || !calc->z80.breakpoints[id].type) {
502 tilem_internal(calc,
503 "attempt to access invalid breakpoint %d", id);
504 return -1;
505 }
506
507 return calc->z80.breakpoints[id].type;
508 }
509
tilem_z80_get_breakpoint_address_start(TilemCalc * calc,int id)510 dword tilem_z80_get_breakpoint_address_start(TilemCalc* calc, int id)
511 {
512 if (id < 1 || id > calc->z80.nbreakpoints
513 || !calc->z80.breakpoints[id].type) {
514 tilem_internal(calc,
515 "attempt to access invalid breakpoint %d", id);
516 return -1;
517 }
518
519 return calc->z80.breakpoints[id].start;
520 }
521
tilem_z80_get_breakpoint_address_end(TilemCalc * calc,int id)522 dword tilem_z80_get_breakpoint_address_end(TilemCalc* calc, int id)
523 {
524 if (id < 1 || id > calc->z80.nbreakpoints
525 || !calc->z80.breakpoints[id].type) {
526 tilem_internal(calc,
527 "attempt to access invalid breakpoint %d", id);
528 return -1;
529 }
530
531 return calc->z80.breakpoints[id].end;
532 }
533
tilem_z80_get_breakpoint_address_mask(TilemCalc * calc,int id)534 dword tilem_z80_get_breakpoint_address_mask(TilemCalc* calc, int id)
535 {
536 if (id < 1 || id > calc->z80.nbreakpoints
537 || !calc->z80.breakpoints[id].type) {
538 tilem_internal(calc,
539 "attempt to access invalid breakpoint %d", id);
540 return -1;
541 }
542
543 return calc->z80.breakpoints[id].mask;
544 }
545
tilem_z80_get_breakpoint_callback(TilemCalc * calc,int id)546 TilemZ80BreakpointFunc tilem_z80_get_breakpoint_callback(TilemCalc* calc, int id)
547 {
548 if (id < 1 || id > calc->z80.nbreakpoints
549 || !calc->z80.breakpoints[id].type) {
550 tilem_internal(calc,
551 "attempt to access invalid breakpoint %d", id);
552 return 0;
553 }
554
555 return calc->z80.breakpoints[id].testfunc;
556 }
557
tilem_z80_get_breakpoint_data(TilemCalc * calc,int id)558 void* tilem_z80_get_breakpoint_data(TilemCalc* calc, int id)
559 {
560 if (id < 1 || id > calc->z80.nbreakpoints
561 || !calc->z80.breakpoints[id].type) {
562 tilem_internal(calc,
563 "attempt to access invalid breakpoint %d", id);
564 return 0;
565 }
566
567 return calc->z80.breakpoints[id].testdata;
568 }
569
tilem_z80_set_breakpoint_type(TilemCalc * calc,int id,int type)570 void tilem_z80_set_breakpoint_type(TilemCalc* calc, int id, int type)
571 {
572 if (id < 1 || id > calc->z80.nbreakpoints
573 || !calc->z80.breakpoints[id].type) {
574 tilem_internal(calc,
575 "attempt to modify invalid breakpoint %d", id);
576 return;
577 }
578
579 if (type == calc->z80.breakpoints[id].type)
580 return;
581
582 bp_rem(calc, id, calc->z80.breakpoints[id].type);
583
584 calc->z80.breakpoints[id].type = type;
585
586 bp_add(calc, id, type);
587 }
588
tilem_z80_set_breakpoint_address_start(TilemCalc * calc,int id,dword start)589 void tilem_z80_set_breakpoint_address_start(TilemCalc* calc, int id, dword start)
590 {
591 if (id < 1 || id > calc->z80.nbreakpoints
592 || !calc->z80.breakpoints[id].type) {
593 tilem_internal(calc,
594 "attempt to modify invalid breakpoint %d", id);
595 return;
596 }
597
598 calc->z80.breakpoints[id].start = start;
599 }
600
tilem_z80_set_breakpoint_address_end(TilemCalc * calc,int id,dword end)601 void tilem_z80_set_breakpoint_address_end(TilemCalc* calc, int id, dword end)
602 {
603 if (id < 1 || id > calc->z80.nbreakpoints
604 || !calc->z80.breakpoints[id].type) {
605 tilem_internal(calc,
606 "attempt to modify invalid breakpoint %d", id);
607 return;
608 }
609
610 calc->z80.breakpoints[id].end = end;
611 }
612
tilem_z80_set_breakpoint_address_mask(TilemCalc * calc,int id,dword mask)613 void tilem_z80_set_breakpoint_address_mask(TilemCalc* calc, int id, dword mask)
614 {
615 if (id < 1 || id > calc->z80.nbreakpoints
616 || !calc->z80.breakpoints[id].type) {
617 tilem_internal(calc,
618 "attempt to modify invalid breakpoint %d", id);
619 return;
620 }
621
622 calc->z80.breakpoints[id].mask = mask;
623 }
624
tilem_z80_set_breakpoint_callback(TilemCalc * calc,int id,TilemZ80BreakpointFunc func)625 void tilem_z80_set_breakpoint_callback(TilemCalc* calc, int id,
626 TilemZ80BreakpointFunc func)
627 {
628 if (id < 1 || id > calc->z80.nbreakpoints
629 || !calc->z80.breakpoints[id].type) {
630 tilem_internal(calc,
631 "attempt to modify invalid breakpoint %d", id);
632 return;
633 }
634
635 calc->z80.breakpoints[id].testfunc = func;
636 }
637
tilem_z80_set_breakpoint_data(TilemCalc * calc,int id,void * data)638 void tilem_z80_set_breakpoint_data(TilemCalc* calc, int id, void* data)
639 {
640 if (id < 1 || id > calc->z80.nbreakpoints
641 || !calc->z80.breakpoints[id].type) {
642 tilem_internal(calc,
643 "attempt to modify invalid breakpoint %d", id);
644 return;
645 }
646
647 calc->z80.breakpoints[id].testdata = data;
648 }
649
650
check_timers(TilemCalc * calc)651 static inline void check_timers(TilemCalc* calc)
652 {
653 int tmr;
654 dword t;
655 TilemZ80TimerFunc callback;
656 void* callbackdata;
657
658 while (calc->z80.timer_cpu) {
659 tmr = calc->z80.timer_cpu;
660 t = calc->z80.clock - calc->z80.timers[tmr].count;
661 if (t >= 10000)
662 break;
663
664 callback = calc->z80.timers[tmr].callback;
665 callbackdata = calc->z80.timers[tmr].callbackdata;
666
667 timer_unset(&calc->z80, tmr);
668 timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period,
669 calc->z80.timers[tmr].period, 0, t);
670
671 (*callback)(calc, callbackdata);
672 }
673
674 while (calc->z80.timer_rt) {
675 tmr = calc->z80.timer_rt;
676 t = calc->z80.clock - calc->z80.timers[tmr].count;
677 if (t >= 10000)
678 break;
679
680 callback = calc->z80.timers[tmr].callback;
681 callbackdata = calc->z80.timers[tmr].callbackdata;
682
683 timer_unset(&calc->z80, tmr);
684 timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period,
685 calc->z80.timers[tmr].period, 1, t);
686
687 (*callback)(calc, callbackdata);
688 }
689 }
690
check_breakpoints(TilemCalc * calc,int list,dword addr)691 static inline void check_breakpoints(TilemCalc* calc, int list, dword addr)
692 {
693 dword masked;
694 int bp;
695 TilemZ80BreakpointFunc testfunc;
696 void* testdata;
697
698 for (bp = list; bp; bp = calc->z80.breakpoints[bp].next) {
699 masked = addr & calc->z80.breakpoints[bp].mask;
700 if (masked < calc->z80.breakpoints[bp].start
701 || masked > calc->z80.breakpoints[bp].end)
702 continue;
703
704 testfunc = calc->z80.breakpoints[bp].testfunc;
705 testdata = calc->z80.breakpoints[bp].testdata;
706
707 if (testfunc && !(*testfunc)(calc, addr, testdata))
708 continue;
709
710 calc->z80.stop_breakpoint = bp;
711 tilem_z80_stop(calc, TILEM_STOP_BREAKPOINT);
712 }
713 }
714
check_mem_breakpoints(TilemCalc * calc,int list_l,int list_p,dword addr)715 static inline void check_mem_breakpoints(TilemCalc* calc, int list_l,
716 int list_p, dword addr)
717 {
718 check_breakpoints(calc, list_l, addr);
719
720 if (list_p) {
721 addr = (*calc->hw.mem_ltop)(calc, addr);
722 check_breakpoints(calc, list_p, addr);
723 }
724 }
725
z80_readb_m1(TilemCalc * calc,dword addr)726 static inline byte z80_readb_m1(TilemCalc* calc, dword addr)
727 {
728 byte b;
729 addr &= 0xffff;
730 b = (*calc->hw.z80_rdmem_m1)(calc, addr);
731 check_mem_breakpoints(calc, calc->z80.breakpoint_mx,
732 calc->z80.breakpoint_mpx, addr);
733 Rl++;
734 return b;
735 }
736
z80_readb(TilemCalc * calc,dword addr)737 static inline byte z80_readb(TilemCalc* calc, dword addr)
738 {
739 byte b;
740 addr &= 0xffff;
741 b = (*calc->hw.z80_rdmem)(calc, addr);
742 check_mem_breakpoints(calc, calc->z80.breakpoint_mr,
743 calc->z80.breakpoint_mpr, addr);
744 return b;
745 }
746
z80_readw(TilemCalc * calc,dword addr)747 static inline dword z80_readw(TilemCalc* calc, dword addr)
748 {
749 dword v;
750 addr &= 0xffff;
751 v = (*calc->hw.z80_rdmem)(calc, addr);
752 check_mem_breakpoints(calc, calc->z80.breakpoint_mr,
753 calc->z80.breakpoint_mpr, addr);
754 addr = (addr + 1) & 0xffff;
755 v |= (*calc->hw.z80_rdmem)(calc, addr) << 8;
756 check_mem_breakpoints(calc, calc->z80.breakpoint_mr,
757 calc->z80.breakpoint_mpr, addr);
758 return v;
759 }
760
z80_input(TilemCalc * calc,dword addr)761 static inline byte z80_input(TilemCalc* calc, dword addr)
762 {
763 byte b;
764 addr &= 0xffff;
765 check_timers(calc);
766 b = (*calc->hw.z80_in)(calc, addr);
767 check_breakpoints(calc, calc->z80.breakpoint_pr, addr);
768 return b;
769 }
770
z80_writeb(TilemCalc * calc,dword addr,byte value)771 static inline void z80_writeb(TilemCalc* calc, dword addr, byte value)
772 {
773 addr &= 0xffff;
774 (*calc->hw.z80_wrmem)(calc, addr, value);
775 check_mem_breakpoints(calc, calc->z80.breakpoint_mw,
776 calc->z80.breakpoint_mpw, addr);
777 calc->z80.lastwrite = calc->z80.clock;
778 }
779
z80_writew(TilemCalc * calc,dword addr,word value)780 static inline void z80_writew(TilemCalc* calc, dword addr, word value)
781 {
782 addr &= 0xffff;
783 (*calc->hw.z80_wrmem)(calc, addr, value);
784 check_mem_breakpoints(calc, calc->z80.breakpoint_mw,
785 calc->z80.breakpoint_mpw, addr);
786 addr = (addr + 1) & 0xffff;
787 value >>= 8;
788 (*calc->hw.z80_wrmem)(calc, addr, value);
789 check_mem_breakpoints(calc, calc->z80.breakpoint_mw,
790 calc->z80.breakpoint_mpw, addr);
791 calc->z80.lastwrite = calc->z80.clock;
792 }
793
z80_output(TilemCalc * calc,dword addr,byte value)794 static inline void z80_output(TilemCalc* calc, dword addr, byte value)
795 {
796 addr &= 0xffff;
797 check_timers(calc);
798 (*calc->hw.z80_out)(calc, addr, value);
799 check_breakpoints(calc, calc->z80.breakpoint_pw, addr);
800 }
801
802 #define readb_m1(aaa) z80_readb_m1(calc, aaa)
803 #define readb(aaa) z80_readb(calc, aaa)
804 #define readw(aaa) z80_readw(calc, aaa)
805 #define input(aaa) z80_input(calc, aaa)
806 #define writeb(aaa, vvv) z80_writeb(calc, aaa, vvv)
807 #define writew(aaa, vvv) z80_writew(calc, aaa, vvv)
808 #define output(aaa, vvv) z80_output(calc, aaa, vvv)
809 #define delay(nnn) calc->z80.clock += (nnn)
810
811 #include "z80cmds.h"
812
z80_execute_opcode(TilemCalc * calc,byte op)813 static dword z80_execute_opcode(TilemCalc* calc, byte op)
814 {
815 byte tmp1;
816 word tmp2;
817 int offs;
818 #ifdef DISABLE_Z80_WZ_REGISTER
819 TilemZ80Reg temp_wz, temp_wz2;
820 #endif
821
822 opcode_main:
823 #include "z80main.h"
824 return op;
825
826 opcode_cb:
827 #include "z80cb.h"
828 return op | 0xcb00;
829
830 opcode_ed:
831 #include "z80ed.h"
832 return op | 0xed00;
833
834 #define PREFIX_DD
835 opcode_dd:
836 #include "z80ddfd.h"
837 return op | 0xdd00;
838 opcode_ddcb:
839 #include "z80cb.h"
840 return op | 0xddcb0000;
841 #undef PREFIX_DD
842
843 #define PREFIX_FD
844 opcode_fd:
845 #include "z80ddfd.h"
846 return op | 0xfd00;
847 opcode_fdcb:
848 #include "z80cb.h"
849 return op | 0xfdcb0000;
850 #undef PREFIX_FD
851 }
852
z80_execute(TilemCalc * calc)853 static void z80_execute(TilemCalc* calc)
854 {
855 TilemZ80* z80 = &calc->z80;
856 byte busbyte;
857 dword op;
858 dword t1, t2;
859
860 z80->stopping = 0;
861 z80->stop_reason = 0;
862 z80->stop_breakpoint = 0;
863
864 if (!z80->timer_cpu && !z80->timer_rt) {
865 tilem_internal(calc, "No timers set");
866 return;
867 }
868
869 while (!z80->stopping) {
870 z80->exception = 0;
871 op = (*calc->hw.z80_rdmem_m1)(calc, PC);
872 PC++;
873 Rl++;
874 op = z80_execute_opcode(calc, op);
875 check_breakpoints(calc, z80->breakpoint_op, op);
876 check_timers(calc);
877
878 if (z80->interrupts && IFF1 && op != 0xfb
879 && op != 0xddfb && op != 0xfdfb) {
880 IFF1 = IFF2 = 0;
881 Rl++;
882 z80->halted = 0;
883
884 /* Depending on the calculator, this value
885 varies somewhat randomly from one interrupt
886 to the next (making IM 2 rather difficult
887 to use, and IM 0 essentially worthless.)
888 Most likely, there is nothing connected to
889 the data bus at interrupt time. I seem to
890 remember somebody (sigma, perhaps?)
891 experimenting with this on the TI-83+ and
892 finding it usually 3F, 7F, BF, or FF. Or
893 maybe I'm completely wrong. In any case it
894 is unwise for programs to depend on this
895 value! */
896
897 busbyte = rand() & 0xff;
898
899 switch (IM) {
900 case 0:
901 delay(2);
902 z80_execute_opcode(calc, busbyte);
903 break;
904
905 case 1:
906 push(PC);
907 PC = 0x0038;
908 delay(13);
909 break;
910
911 case 2:
912 /* FIXME: does accepting an IM 2
913 interrupt affect WZ? It seems very
914 likely. */
915 push(PC);
916 PC = readw((IR & 0xff00) | busbyte);
917 delay(19);
918 }
919 check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC);
920 check_timers(calc);
921 }
922 else if (op != 0x76) {
923 check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC);
924 }
925 else {
926 z80->halted = 1;
927 PC--;
928 if (z80->stopping)
929 break;
930
931 /* CPU halted: fast-forward to next timer event */
932 if (z80->timer_cpu && z80->timer_rt) {
933 t1 = (z80->timers[z80->timer_cpu].count
934 - z80->clock);
935 t2 = (z80->timers[z80->timer_rt].count
936 - z80->clock);
937 if (t1 > t2)
938 t1 = t2;
939 }
940 else if (z80->timer_cpu) {
941 t1 = (z80->timers[z80->timer_cpu].count
942 - z80->clock);
943 }
944 else if (z80->timer_rt) {
945 t1 = (z80->timers[z80->timer_rt].count
946 - z80->clock);
947 }
948 else {
949 tilem_internal(calc, "No timers set");
950 return;
951 }
952
953 z80->clock += t1 & ~3;
954 Rl += t1 / 4;
955 check_timers(calc);
956 }
957
958 if (TILEM_UNLIKELY(z80->exception)) {
959 if (z80->emuflags & TILEM_Z80_BREAK_EXCEPTIONS)
960 tilem_z80_stop(calc, TILEM_STOP_EXCEPTION);
961 if (!(z80->emuflags & TILEM_Z80_IGNORE_EXCEPTIONS))
962 tilem_calc_reset(calc);
963 }
964 }
965 }
966
tmr_stop(TilemCalc * calc,void * data TILEM_ATTR_UNUSED)967 static void tmr_stop(TilemCalc* calc, void* data TILEM_ATTR_UNUSED)
968 {
969 tilem_z80_stop(calc, TILEM_STOP_TIMEOUT);
970 }
971
tilem_z80_run(TilemCalc * calc,int clocks,int * remaining)972 dword tilem_z80_run(TilemCalc* calc, int clocks, int* remaining)
973 {
974 int tmr = tilem_z80_add_timer(calc, clocks, 0, 0, &tmr_stop, 0);
975 z80_execute(calc);
976 if (remaining)
977 *remaining = tilem_z80_get_timer_clocks(calc, tmr);
978 tilem_z80_remove_timer(calc, tmr);
979 return calc->z80.stop_reason;
980 }
981
tilem_z80_run_time(TilemCalc * calc,int microseconds,int * remaining)982 dword tilem_z80_run_time(TilemCalc* calc, int microseconds, int* remaining)
983 {
984 int tmr = tilem_z80_add_timer(calc, microseconds, 0, 1, &tmr_stop, 0);
985 z80_execute(calc);
986 if (remaining)
987 *remaining = tilem_z80_get_timer_microseconds(calc, tmr);
988 tilem_z80_remove_timer(calc, tmr);
989 return calc->z80.stop_reason;
990 }
991