/*
* 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;
}