/* * libtilemcore - Graphing calculator emulation library * * Copyright (C) 2009-2011 Benjamin Moody * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "tilem.h" #include "z80.h" /* Timer manipulation */ /* static void dumptimers(TilemZ80* z80) { int tmr; int t; printf("*** RT:"); for (tmr = z80->timer_rt; tmr; tmr = z80->timers[tmr].next) { t = z80->timers[tmr].count - z80->clock; printf(" %d:%d", tmr, t); } printf("\n*** CPU:"); for (tmr = z80->timer_cpu; tmr; tmr = z80->timers[tmr].next) { t = z80->timers[tmr].count - z80->clock; printf(" %d:%d", tmr, t); } printf("\n*** Free:"); for (tmr = z80->timer_free; tmr; tmr = z80->timers[tmr].next) { printf(" %d", tmr); } printf("\n"); } */ static inline void timer_free(TilemZ80* z80, int tmr) { z80->timers[tmr].callback = NULL; z80->timers[tmr].callbackdata = NULL; z80->timers[tmr].next = z80->timer_free; z80->timers[tmr].prev = 0; z80->timer_free = tmr; } static inline int timer_alloc(TilemZ80* z80) { int tmr, i; if (z80->timer_free) { tmr = z80->timer_free; z80->timer_free = z80->timers[tmr].next; z80->timers[tmr].next = 0; return tmr; } i = z80->ntimers; z80->ntimers = i * 2 + 1; z80->timers = tilem_renew(TilemZ80Timer, z80->timers, z80->ntimers); while (i < z80->ntimers) { timer_free(z80, i); i++; } tmr = z80->timer_free; z80->timer_free = z80->timers[tmr].next; z80->timers[tmr].next = 0; return tmr; } static inline int timer_earlier(TilemZ80* z80, int tmr1, int tmr2) { dword count1, count2; count1 = z80->timers[tmr1].count + 10000 - z80->clock; count2 = z80->timers[tmr2].count + 10000 - z80->clock; return (count1 < count2); } static inline void timer_insert(TilemZ80* z80, int* list, int tmr) { int prev, next; if (!*list || timer_earlier(z80, tmr, *list)) { z80->timers[tmr].prev = 0; z80->timers[tmr].next = *list; z80->timers[*list].prev = tmr; *list = tmr; return; } prev = *list; next = z80->timers[prev].next; while (next && timer_earlier(z80, next, tmr)) { prev = next; next = z80->timers[prev].next; } z80->timers[prev].next = tmr; z80->timers[next].prev = tmr; z80->timers[tmr].prev = prev; z80->timers[tmr].next = next; } static inline void timer_set(TilemZ80* z80, int tmr, dword count, dword period, int rt, dword extra) { dword clocks; qword kclocks; if (!count) { /* leave timer disabled */ z80->timers[tmr].prev = 0; z80->timers[tmr].next = 0; } else if (rt) { kclocks = z80->clockspeed; kclocks *= count; clocks = (kclocks + 500) / 1000 - extra; z80->timers[tmr].count = z80->clock + clocks; z80->timers[tmr].period = period; timer_insert(z80, &z80->timer_rt, tmr); } else { clocks = count - extra; z80->timers[tmr].count = z80->clock + clocks; z80->timers[tmr].period = period; timer_insert(z80, &z80->timer_cpu, tmr); } } static inline void timer_unset(TilemZ80* z80, int tmr) { int prev, next; if (tmr == z80->timer_cpu) z80->timer_cpu = z80->timers[tmr].next; if (tmr == z80->timer_rt) z80->timer_rt = z80->timers[tmr].next; prev = z80->timers[tmr].prev; next = z80->timers[tmr].next; z80->timers[prev].next = next; z80->timers[next].prev = prev; z80->timers[tmr].prev = 0; z80->timers[tmr].next = 0; } /* Breakpoint manipulation */ static inline void bp_free(TilemZ80* z80, int bp) { z80->breakpoints[bp].type = 0; z80->breakpoints[bp].testfunc = NULL; z80->breakpoints[bp].testdata = NULL; z80->breakpoints[bp].next = z80->breakpoint_free; z80->breakpoints[bp].prev = 0; z80->breakpoint_free = bp; } static inline int bp_alloc(TilemZ80* z80) { int bp, i; if (z80->breakpoint_free) { bp = z80->breakpoint_free; z80->breakpoint_free = z80->breakpoints[bp].next; return bp; } i = z80->nbreakpoints; z80->nbreakpoints = i * 2 + 2; z80->breakpoints = tilem_renew(TilemZ80Breakpoint, z80->breakpoints, z80->nbreakpoints); while (i < z80->nbreakpoints) { bp_free(z80, i); i++; } bp = z80->breakpoint_free; z80->breakpoint_free = z80->breakpoints[bp].next; return bp; } static int* bp_head(TilemCalc *calc, int type) { if (type & TILEM_BREAK_DISABLED) return &calc->z80.breakpoint_disabled; switch (type & TILEM_BREAK_TYPE_MASK) { case TILEM_BREAK_MEM_READ: return (type & TILEM_BREAK_PHYSICAL ? &calc->z80.breakpoint_mpr : &calc->z80.breakpoint_mr); case TILEM_BREAK_MEM_EXEC: return (type & TILEM_BREAK_PHYSICAL ? &calc->z80.breakpoint_mpx : &calc->z80.breakpoint_mx); case TILEM_BREAK_MEM_WRITE: return (type & TILEM_BREAK_PHYSICAL ? &calc->z80.breakpoint_mpw : &calc->z80.breakpoint_mw); case TILEM_BREAK_PORT_READ: return &calc->z80.breakpoint_pr; case TILEM_BREAK_PORT_WRITE: return &calc->z80.breakpoint_pw; case TILEM_BREAK_EXECUTE: return &calc->z80.breakpoint_op; default: tilem_internal(calc, "invalid bp type"); return 0; } } static int bp_add(TilemCalc *calc, int bp, int type) { int *head = bp_head(calc, type); if (!head) { bp_free(&calc->z80, bp); return 0; } calc->z80.breakpoints[bp].next = *head; calc->z80.breakpoints[*head].prev = *head ? bp : 0; *head = bp; return bp; } static void bp_rem(TilemCalc *calc, int bp, int type) { int prev, next; int *head = bp_head(calc, type); prev = calc->z80.breakpoints[bp].prev; next = calc->z80.breakpoints[bp].next; if (bp == *head) *head = next; calc->z80.breakpoints[prev].next = prev ? next : 0; calc->z80.breakpoints[next].prev = next ? prev : 0; } static void invoke_ptimer(TilemCalc* calc, void* data) { (*calc->hw.z80_ptimer)(calc, TILEM_PTR_TO_DWORD(data)); } /* Z80 API */ void tilem_z80_reset(TilemCalc* calc) { int i; AF = BC = DE = HL = AF2 = BC2 = DE2 = HL2 = 0xffff; IX = IY = IR = SP = WZ = WZ2 = 0xffff; PC = 0; Rh = 0x80; IFF1 = IFF2 = IM = 0; calc->z80.interrupts = 0; calc->z80.halted = 0; /* Set up hardware timers */ if (!calc->z80.ntimers) { calc->z80.ntimers = (calc->hw.nhwtimers + TILEM_NUM_SYS_TIMERS + 1); tilem_free(calc->z80.timers); calc->z80.timers = tilem_new(TilemZ80Timer, calc->z80.ntimers); for (i = 1; i < calc->z80.ntimers; i++) { calc->z80.timers[i].next = 0; calc->z80.timers[i].prev = 0; calc->z80.timers[i].count = 0; calc->z80.timers[i].period = 0; calc->z80.timers[i].callback = &invoke_ptimer; calc->z80.timers[i].callbackdata = TILEM_DWORD_TO_PTR(i); } calc->z80.timers[TILEM_TIMER_LCD_DELAY].callback = &tilem_lcd_delay_timer; calc->z80.timers[TILEM_TIMER_FLASH_DELAY].callback = tilem_flash_delay_timer; calc->z80.timers[TILEM_TIMER_LINK_ASSIST].callback = tilem_linkport_assist_timer; for (i = 0; i < TILEM_MAX_USER_TIMERS; i++) { calc->z80.timers[TILEM_TIMER_USER1 + i].callback = tilem_user_timer_expired; calc->z80.timers[TILEM_TIMER_USER1 + i].callbackdata = TILEM_DWORD_TO_PTR(i); } } } void tilem_z80_stop(TilemCalc* calc, dword reason) { if (!(reason & calc->z80.stop_mask)) { calc->z80.stop_reason |= reason; calc->z80.stopping = 1; } } void tilem_z80_exception(TilemCalc* calc, unsigned type) { calc->z80.exception |= type; } void tilem_z80_set_speed(TilemCalc* calc, int speed) { int tmr; qword t; int oldspeed = calc->z80.clockspeed; if (oldspeed == speed) return; for (tmr = calc->z80.timer_rt; tmr; tmr = calc->z80.timers[tmr].next) { if ((calc->z80.clock - calc->z80.timers[tmr].count) < 10000) continue; t = calc->z80.timers[tmr].count - calc->z80.clock; t = (t * speed + oldspeed / 2) / oldspeed; calc->z80.timers[tmr].count = calc->z80.clock + t; } calc->z80.clockspeed = speed; } int tilem_z80_add_timer(TilemCalc* calc, dword count, dword period, int rt, TilemZ80TimerFunc func, void* data) { int id; id = timer_alloc(&calc->z80); calc->z80.timers[id].callback = func; calc->z80.timers[id].callbackdata = data; timer_set(&calc->z80, id, count, period, rt, 0); return id; } void tilem_z80_set_timer(TilemCalc* calc, int id, dword count, dword period, int rt) { if (id < 1 || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { tilem_internal(calc, "setting invalid timer %d", id); return; } timer_unset(&calc->z80, id); timer_set(&calc->z80, id, count, period, rt, 0); } void tilem_z80_set_timer_period(TilemCalc* calc, int id, dword period) { if (id < 1 || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { tilem_internal(calc, "setting invalid timer %d", id); return; } calc->z80.timers[id].period = period; } void tilem_z80_remove_timer(TilemCalc* calc, int id) { if (id <= calc->hw.nhwtimers + TILEM_NUM_SYS_TIMERS || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { tilem_internal(calc, "removing invalid timer %d", id); return; } timer_unset(&calc->z80, id); timer_free(&calc->z80, id); } int tilem_z80_timer_running(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { tilem_internal(calc, "querying invalid timer %d", id); return 0; } if (id == calc->z80.timer_rt || id == calc->z80.timer_cpu) return 1; if (calc->z80.timers[id].prev) return 1; return 0; } int tilem_z80_get_timer_clocks(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { tilem_internal(calc, "querying invalid timer %d", id); return 0; } return (calc->z80.timers[id].count - calc->z80.clock); } int tilem_z80_get_timer_microseconds(TilemCalc* calc, int id) { int n = tilem_z80_get_timer_clocks(calc, id); if (n < 0) { n = ((((qword) -n * 1000) + (calc->z80.clockspeed / 2)) / calc->z80.clockspeed); return -n; } else { n = ((((qword) n * 1000) + (calc->z80.clockspeed / 2)) / calc->z80.clockspeed); return n; } } int tilem_z80_add_breakpoint(TilemCalc* calc, int type, dword start, dword end, dword mask, TilemZ80BreakpointFunc func, void* data) { int bp; bp = bp_alloc(&calc->z80); calc->z80.breakpoints[bp].type = type; calc->z80.breakpoints[bp].start = start; calc->z80.breakpoints[bp].end = end; calc->z80.breakpoints[bp].mask = mask; calc->z80.breakpoints[bp].testfunc = func; calc->z80.breakpoints[bp].testdata = data; calc->z80.breakpoints[bp].prev = 0; return bp_add(calc, bp, type); } void tilem_z80_remove_breakpoint(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to remove invalid breakpoint %d", id); return; } bp_rem(calc, id, calc->z80.breakpoints[id].type); bp_free(&calc->z80, id); } void tilem_z80_enable_breakpoint(TilemCalc* calc, int id) { int type = tilem_z80_get_breakpoint_type(calc, id); tilem_z80_set_breakpoint_type(calc, id, type & ~TILEM_BREAK_DISABLED); } void tilem_z80_disable_breakpoint(TilemCalc* calc, int id) { int type = tilem_z80_get_breakpoint_type(calc, id); tilem_z80_set_breakpoint_type(calc, id, type | TILEM_BREAK_DISABLED); } int tilem_z80_breakpoint_enabled(TilemCalc* calc, int id) { int type = tilem_z80_get_breakpoint_type(calc, id); return !(type & TILEM_BREAK_DISABLED); } int tilem_z80_get_breakpoint_type(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return -1; } return calc->z80.breakpoints[id].type; } dword tilem_z80_get_breakpoint_address_start(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return -1; } return calc->z80.breakpoints[id].start; } dword tilem_z80_get_breakpoint_address_end(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return -1; } return calc->z80.breakpoints[id].end; } dword tilem_z80_get_breakpoint_address_mask(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return -1; } return calc->z80.breakpoints[id].mask; } TilemZ80BreakpointFunc tilem_z80_get_breakpoint_callback(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return 0; } return calc->z80.breakpoints[id].testfunc; } void* tilem_z80_get_breakpoint_data(TilemCalc* calc, int id) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to access invalid breakpoint %d", id); return 0; } return calc->z80.breakpoints[id].testdata; } void tilem_z80_set_breakpoint_type(TilemCalc* calc, int id, int type) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } if (type == calc->z80.breakpoints[id].type) return; bp_rem(calc, id, calc->z80.breakpoints[id].type); calc->z80.breakpoints[id].type = type; bp_add(calc, id, type); } void tilem_z80_set_breakpoint_address_start(TilemCalc* calc, int id, dword start) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } calc->z80.breakpoints[id].start = start; } void tilem_z80_set_breakpoint_address_end(TilemCalc* calc, int id, dword end) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } calc->z80.breakpoints[id].end = end; } void tilem_z80_set_breakpoint_address_mask(TilemCalc* calc, int id, dword mask) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } calc->z80.breakpoints[id].mask = mask; } void tilem_z80_set_breakpoint_callback(TilemCalc* calc, int id, TilemZ80BreakpointFunc func) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } calc->z80.breakpoints[id].testfunc = func; } void tilem_z80_set_breakpoint_data(TilemCalc* calc, int id, void* data) { if (id < 1 || id > calc->z80.nbreakpoints || !calc->z80.breakpoints[id].type) { tilem_internal(calc, "attempt to modify invalid breakpoint %d", id); return; } calc->z80.breakpoints[id].testdata = data; } static inline void check_timers(TilemCalc* calc) { int tmr; dword t; TilemZ80TimerFunc callback; void* callbackdata; while (calc->z80.timer_cpu) { tmr = calc->z80.timer_cpu; t = calc->z80.clock - calc->z80.timers[tmr].count; if (t >= 10000) break; callback = calc->z80.timers[tmr].callback; callbackdata = calc->z80.timers[tmr].callbackdata; timer_unset(&calc->z80, tmr); timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period, calc->z80.timers[tmr].period, 0, t); (*callback)(calc, callbackdata); } while (calc->z80.timer_rt) { tmr = calc->z80.timer_rt; t = calc->z80.clock - calc->z80.timers[tmr].count; if (t >= 10000) break; callback = calc->z80.timers[tmr].callback; callbackdata = calc->z80.timers[tmr].callbackdata; timer_unset(&calc->z80, tmr); timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period, calc->z80.timers[tmr].period, 1, t); (*callback)(calc, callbackdata); } } static inline void check_breakpoints(TilemCalc* calc, int list, dword addr) { dword masked; int bp; TilemZ80BreakpointFunc testfunc; void* testdata; for (bp = list; bp; bp = calc->z80.breakpoints[bp].next) { masked = addr & calc->z80.breakpoints[bp].mask; if (masked < calc->z80.breakpoints[bp].start || masked > calc->z80.breakpoints[bp].end) continue; testfunc = calc->z80.breakpoints[bp].testfunc; testdata = calc->z80.breakpoints[bp].testdata; if (testfunc && !(*testfunc)(calc, addr, testdata)) continue; calc->z80.stop_breakpoint = bp; tilem_z80_stop(calc, TILEM_STOP_BREAKPOINT); } } static inline void check_mem_breakpoints(TilemCalc* calc, int list_l, int list_p, dword addr) { check_breakpoints(calc, list_l, addr); if (list_p) { addr = (*calc->hw.mem_ltop)(calc, addr); check_breakpoints(calc, list_p, addr); } } static inline byte z80_readb_m1(TilemCalc* calc, dword addr) { byte b; addr &= 0xffff; b = (*calc->hw.z80_rdmem_m1)(calc, addr); check_mem_breakpoints(calc, calc->z80.breakpoint_mx, calc->z80.breakpoint_mpx, addr); Rl++; return b; } static inline byte z80_readb(TilemCalc* calc, dword addr) { byte b; addr &= 0xffff; b = (*calc->hw.z80_rdmem)(calc, addr); check_mem_breakpoints(calc, calc->z80.breakpoint_mr, calc->z80.breakpoint_mpr, addr); return b; } static inline dword z80_readw(TilemCalc* calc, dword addr) { dword v; addr &= 0xffff; v = (*calc->hw.z80_rdmem)(calc, addr); check_mem_breakpoints(calc, calc->z80.breakpoint_mr, calc->z80.breakpoint_mpr, addr); addr = (addr + 1) & 0xffff; v |= (*calc->hw.z80_rdmem)(calc, addr) << 8; check_mem_breakpoints(calc, calc->z80.breakpoint_mr, calc->z80.breakpoint_mpr, addr); return v; } static inline byte z80_input(TilemCalc* calc, dword addr) { byte b; addr &= 0xffff; check_timers(calc); b = (*calc->hw.z80_in)(calc, addr); check_breakpoints(calc, calc->z80.breakpoint_pr, addr); return b; } static inline void z80_writeb(TilemCalc* calc, dword addr, byte value) { addr &= 0xffff; (*calc->hw.z80_wrmem)(calc, addr, value); check_mem_breakpoints(calc, calc->z80.breakpoint_mw, calc->z80.breakpoint_mpw, addr); calc->z80.lastwrite = calc->z80.clock; } static inline void z80_writew(TilemCalc* calc, dword addr, word value) { addr &= 0xffff; (*calc->hw.z80_wrmem)(calc, addr, value); check_mem_breakpoints(calc, calc->z80.breakpoint_mw, calc->z80.breakpoint_mpw, addr); addr = (addr + 1) & 0xffff; value >>= 8; (*calc->hw.z80_wrmem)(calc, addr, value); check_mem_breakpoints(calc, calc->z80.breakpoint_mw, calc->z80.breakpoint_mpw, addr); calc->z80.lastwrite = calc->z80.clock; } static inline void z80_output(TilemCalc* calc, dword addr, byte value) { addr &= 0xffff; check_timers(calc); (*calc->hw.z80_out)(calc, addr, value); check_breakpoints(calc, calc->z80.breakpoint_pw, addr); } #define readb_m1(aaa) z80_readb_m1(calc, aaa) #define readb(aaa) z80_readb(calc, aaa) #define readw(aaa) z80_readw(calc, aaa) #define input(aaa) z80_input(calc, aaa) #define writeb(aaa, vvv) z80_writeb(calc, aaa, vvv) #define writew(aaa, vvv) z80_writew(calc, aaa, vvv) #define output(aaa, vvv) z80_output(calc, aaa, vvv) #define delay(nnn) calc->z80.clock += (nnn) #include "z80cmds.h" static dword z80_execute_opcode(TilemCalc* calc, byte op) { byte tmp1; word tmp2; int offs; #ifdef DISABLE_Z80_WZ_REGISTER TilemZ80Reg temp_wz, temp_wz2; #endif opcode_main: #include "z80main.h" return op; opcode_cb: #include "z80cb.h" return op | 0xcb00; opcode_ed: #include "z80ed.h" return op | 0xed00; #define PREFIX_DD opcode_dd: #include "z80ddfd.h" return op | 0xdd00; opcode_ddcb: #include "z80cb.h" return op | 0xddcb0000; #undef PREFIX_DD #define PREFIX_FD opcode_fd: #include "z80ddfd.h" return op | 0xfd00; opcode_fdcb: #include "z80cb.h" return op | 0xfdcb0000; #undef PREFIX_FD } static void z80_execute(TilemCalc* calc) { TilemZ80* z80 = &calc->z80; byte busbyte; dword op; dword t1, t2; z80->stopping = 0; z80->stop_reason = 0; z80->stop_breakpoint = 0; if (!z80->timer_cpu && !z80->timer_rt) { tilem_internal(calc, "No timers set"); return; } while (!z80->stopping) { z80->exception = 0; op = (*calc->hw.z80_rdmem_m1)(calc, PC); PC++; Rl++; op = z80_execute_opcode(calc, op); check_breakpoints(calc, z80->breakpoint_op, op); check_timers(calc); if (z80->interrupts && IFF1 && op != 0xfb && op != 0xddfb && op != 0xfdfb) { IFF1 = IFF2 = 0; Rl++; z80->halted = 0; /* Depending on the calculator, this value varies somewhat randomly from one interrupt to the next (making IM 2 rather difficult to use, and IM 0 essentially worthless.) Most likely, there is nothing connected to the data bus at interrupt time. I seem to remember somebody (sigma, perhaps?) experimenting with this on the TI-83+ and finding it usually 3F, 7F, BF, or FF. Or maybe I'm completely wrong. In any case it is unwise for programs to depend on this value! */ busbyte = rand() & 0xff; switch (IM) { case 0: delay(2); z80_execute_opcode(calc, busbyte); break; case 1: push(PC); PC = 0x0038; delay(13); break; case 2: /* FIXME: does accepting an IM 2 interrupt affect WZ? It seems very likely. */ push(PC); PC = readw((IR & 0xff00) | busbyte); delay(19); } check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC); check_timers(calc); } else if (op != 0x76) { check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC); } else { z80->halted = 1; PC--; if (z80->stopping) break; /* CPU halted: fast-forward to next timer event */ if (z80->timer_cpu && z80->timer_rt) { t1 = (z80->timers[z80->timer_cpu].count - z80->clock); t2 = (z80->timers[z80->timer_rt].count - z80->clock); if (t1 > t2) t1 = t2; } else if (z80->timer_cpu) { t1 = (z80->timers[z80->timer_cpu].count - z80->clock); } else if (z80->timer_rt) { t1 = (z80->timers[z80->timer_rt].count - z80->clock); } else { tilem_internal(calc, "No timers set"); return; } z80->clock += t1 & ~3; Rl += t1 / 4; check_timers(calc); } if (TILEM_UNLIKELY(z80->exception)) { if (z80->emuflags & TILEM_Z80_BREAK_EXCEPTIONS) tilem_z80_stop(calc, TILEM_STOP_EXCEPTION); if (!(z80->emuflags & TILEM_Z80_IGNORE_EXCEPTIONS)) tilem_calc_reset(calc); } } } static void tmr_stop(TilemCalc* calc, void* data TILEM_ATTR_UNUSED) { tilem_z80_stop(calc, TILEM_STOP_TIMEOUT); } dword tilem_z80_run(TilemCalc* calc, int clocks, int* remaining) { int tmr = tilem_z80_add_timer(calc, clocks, 0, 0, &tmr_stop, 0); z80_execute(calc); if (remaining) *remaining = tilem_z80_get_timer_clocks(calc, tmr); tilem_z80_remove_timer(calc, tmr); return calc->z80.stop_reason; } dword tilem_z80_run_time(TilemCalc* calc, int microseconds, int* remaining) { int tmr = tilem_z80_add_timer(calc, microseconds, 0, 1, &tmr_stop, 0); z80_execute(calc); if (remaining) *remaining = tilem_z80_get_timer_microseconds(calc, tmr); tilem_z80_remove_timer(calc, tmr); return calc->z80.stop_reason; }