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