#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "mfpemul.h"
#include "emu68/assert68.h"
#include <sc68/msg68.h>
#define cpp(V) (V*prediv_width[(int)ptimer->tcr])
#define timerfrq(V) ((8000000u*192u)/cpp(V))
#define MFP_VECTOR_BASE (mfp->map[0x17] & 0xF0)
#define SEI (mfp->map[0x17] & 0x08)
#define AEI (!SEI)
#ifndef DEBUG_MFP_O
# define DEBUG_MFP_O 0
#endif
int mfp_cat = msg68_DEFAULT;
#undef _MFPIO68_SUPER_EMUL_
static const mfp_timer_def_t timer_def[4] =
{
{ 0x34>>2, 6, 1<<5, 0, 'A' },
{ 0x20>>2, 6, 1<<0, 0, 'B' },
{ 0x14>>2, 6, 1<<5, 2, 'C' },
{ 0x10>>2, 6, 1<<4, 2, 'D' },
};
static const bogoc68_t prediv_width[8] = {
0*625, 4*625, 10*625, 16*625,
50*625, 64*625, 100*625, 200*625
};
static mfp_timer_t * find_next_int(const mfp_t * const mfp)
{
const mfp_timer_t *ptimer, *itimer;
for(itimer=0, ptimer=mfp->timers;
ptimer<mfp->timers+4 && !ptimer->tcr;
++ptimer)
;
if (ptimer < mfp->timers+4) {
bogoc68_t cti = ptimer->cti;
itimer = ptimer;
while (++ptimer < mfp->timers+4) {
if (ptimer->tcr && ptimer->cti<cti) {
cti = ptimer->cti;
itimer = ptimer;
}
}
}
return (mfp_timer_t *)itimer;
}
static inline
int timer_get_tdr(const mfp_timer_t * const ptimer, const bogoc68_t bogoc)
{
const bogoc68_t cti = ptimer->cti - bogoc;
const uint68_t psw = prediv_width[ptimer->tcr];
const uint68_t cnt = cti/psw;
const uint68_t tdr = cnt%ptimer->tdr_res+1;
return tdr;
}
static inline
void reconf_timer(mfp_timer_t * const ptimer, int tcr, const bogoc68_t bogoc)
{
const bogoc68_t cti = ptimer->cti - bogoc;
const uint_t psw = prediv_width[ptimer->tcr];
const uint_t cnt = cti/psw;
const uint_t psr = cti % psw;
const uint_t tdr = cnt+1;
const cycle68_t new_psw = prediv_width[(int)tcr];
if (bogoc > ptimer->cti) {
TRACE68(mfp_cat,
"mfp: timer-%c -- reconf out of range -- @%u > cti:%u\n",
ptimer->def.letter, bogoc, ptimer->cti);
ptimer->cti = bogoc + psw * ptimer->tdr_res;
} else {
ptimer->cti = bogoc + psr + (tdr-1) * new_psw;
ptimer->cti = bogoc + (tdr) * new_psw;
}
ptimer->tcr = tcr;
TRACE68(mfp_cat,
"mfp: timer-%c -- reconf @%u cti:%u cpp:%u -- %d:%dhz\n",
ptimer->def.letter, bogoc,
ptimer->cti, cpp(ptimer->tdr_res),
0,timerfrq(ptimer->tdr_res));
}
static inline
void stop_timer(mfp_timer_t * const ptimer, const bogoc68_t bogoc)
{
ptimer->tdr_cur = timer_get_tdr(ptimer, bogoc);
ptimer->tcr = 0;
ptimer->psc = 0;
}
static inline
void resume_timer(mfp_timer_t * const ptimer, int tcr, bogoc68_t bogoc)
{
ptimer->tcr = tcr;
ptimer->cti = bogoc + ptimer->tdr_cur * prediv_width[tcr] - ptimer->psc;
TRACE68(mfp_cat,
"mfp: timer-%c -- resume @%u cti:%u cpp:%u "
"tdr:%u/%u psw:%u(%u) -- %dhz\n",
ptimer->def.letter, bogoc, ptimer->cti,
cpp(ptimer->tdr_res),
(int)ptimer->tdr_cur,(int)ptimer->tdr_res,
prediv_width[ptimer->tcr],ptimer->tcr,
timerfrq(ptimer->tdr_res));
}
int68_t mfp_get_tdr(mfp_t * const mfp, const int timer, const bogoc68_t bogoc)
{
mfp_timer_t * const ptimer = &mfp->timers[timer&3];
if (ptimer->tcr) {
ptimer->tdr_cur = timer_get_tdr(ptimer, bogoc);
}
return(u8)ptimer->tdr_cur;
}
void mfp_put_tdr(mfp_t * const mfp, int timer, int68_t v, bogoc68_t bogoc)
{
mfp_timer_t * const ptimer = &mfp->timers[timer&3];
const uint_t old_tdr = ptimer->tdr_res;
v = (u8)v; v += (!v)<<8;
ptimer->tdr_res = v;
if (!ptimer->tcr) {
ptimer->tdr_cur = v;
TRACE68(mfp_cat,
"mfp: timer-%c -- reload TDR @%u -- %u\n",
ptimer->def.letter, bogoc, ptimer->tdr_res);
} else if (ptimer->tcr && v != old_tdr) {
TRACE68(mfp_cat,
"mfp: timer-%c -- change @%u cti:%u psw:%u(%u) cpp:%u"
" -- %u(%u) -> %u(%u)hz\n",
ptimer->def.letter, bogoc, ptimer->cti,
prediv_width[ptimer->tcr], ptimer->tcr,
cpp(ptimer->tdr_res),
0,old_tdr,
timerfrq(ptimer->tdr_res), ptimer->tdr_res);
}
}
static void mfp_put_tcr_bogo(mfp_timer_t * const ptimer,
int v, const bogoc68_t bogoc)
{
if (v != ptimer->tcr) {
if (!v) {
stop_timer(ptimer,bogoc);
} else if (!ptimer->tcr) {
resume_timer(ptimer, v, bogoc);
} else {
reconf_timer(ptimer, v, bogoc);
}
}
}
void mfp_put_tcr(mfp_t * const mfp,
int timer, int68_t v, const bogoc68_t bogoc)
{
timer &= 3;
if (timer < TIMER_C) {
mfp->map[0x19+2*timer] = v;
if (v&0x10) {
TRACE68(mfp_cat,
"mfp: timer-%c -- mode not supported -- %02x\n",
timer_def[timer].letter,(int)(u8)v);
}
mfp_put_tcr_bogo(mfp->timers+timer, v&7, bogoc);
} else {
mfp->map[0x1D] = v;
mfp_put_tcr_bogo(mfp->timers+TIMER_C, (v>>4)&7, bogoc);
mfp_put_tcr_bogo(mfp->timers+TIMER_D, v &7, bogoc);
}
}
bogoc68_t mfp_nextinterrupt(const mfp_t * const mfp)
{
const mfp_timer_t * itimer = find_next_int(mfp);
const bogoc68_t bogoc =
(!itimer) ? IO68_NO_INT : itimer->cti;
return bogoc;
}
static inline
void timer_interrupt(mfp_t * const mfp, mfp_timer_t * const ptimer)
{
ptimer->interrupt.vector = MFP_VECTOR_BASE + ptimer->def.vector;
ptimer->interrupt.level = ptimer->def.level;
ptimer->interrupt.cycle = ptimer->cti;
ptimer->cti += prediv_width[ptimer->tcr] * ptimer->tdr_res;
ptimer->tdr_cur = ptimer->tdr_res;
}
interrupt68_t * mfp_interrupt(mfp_t * const mfp, const bogoc68_t bogoc)
{
mfp_timer_t * itimer;
while ((itimer = find_next_int(mfp)) && itimer->cti < bogoc) {
mfp_timer_t * const ptimer = itimer;
timer_interrupt(mfp,ptimer);
#ifndef _MFPIO68_SUPER_EMUL_
if (mfp->map[0x07+ptimer->def.channel] &
mfp->map[0x13+ptimer->def.channel] &
ptimer->def.bit )
{
++ptimer->int_fall;
return &ptimer->interrupt;
}
#else
if ( mfp->map[0x07+ptimer->channel] & ptimer->bit ) {
mfp->map[0x0B+ptimer->channel] |= ptimer->bit;
if (!(mfp->map[0x0F+ptimer->channel]&ptimer->bit)) {
if (SEI) mfp->map[0x0B+ptimer->channel] |= ptimer->bit;
if (mfp->map[0x13+ptimer->channel] & ptimer->bit) {
++ptimer->int_fall;
return &ptimer->interrupt;
}
}
}
#endif
++ptimer->int_mask;
}
return 0;
}
void mfp_adjust_bogoc(mfp_t * const mfp, const bogoc68_t bogoc)
{
mfp_timer_t *ptimer;
if (!bogoc) return;
for (ptimer = mfp->timers; ptimer != mfp->timers+4; ++ptimer) {
if (ptimer->tcr) {
if (ptimer->cti < bogoc) {
TRACE68(mfp_cat,
"mfp: timer-%c -- adjust -- cti:%u cycle:%u\n",
ptimer->def.letter,ptimer->cti, bogoc);
}
assert(ptimer->cti >= bogoc);
while (ptimer->cti < bogoc) {
++ptimer->int_lost;
ptimer->cti += cpp(ptimer->tdr_res);
}
if (ptimer->int_lost) {
msg68_critical("mfp: timer-%c -- adjust has lost interrupt -- %d\n",
ptimer->def.letter, ptimer->int_lost);
ptimer->int_lost = 0;
}
ptimer->cti -= bogoc;
}
}
}
static void reset_timer(mfp_timer_t * const ptimer, bogoc68_t bogoc)
{
ptimer->cti = bogoc;
ptimer->tcr = 0;
ptimer->psc = 0;
ptimer->tdr_res = 256;
ptimer->tdr_cur = ptimer->tdr_res;
ptimer->int_lost = 0;
ptimer->int_mask = 0;
ptimer->int_fall = 0;
}
static int mfp_reset_bogo(mfp_t * const mfp, const bogoc68_t bogoc)
{
int i;
for (i=0; i<(int)sizeof(mfp->map); i++) {
mfp->map[i]=0;
}
mfp->map[0x17] = 0x40;
mfp->map[0x01] = 0x80;
for (i=0; i<4;i++) {
reset_timer(mfp->timers+i, bogoc);
}
return 0;
}
int mfp_reset(mfp_t * const mfp, const bogoc68_t bogoc)
{
return mfp_reset_bogo(mfp, bogoc);
}
static void setup_timer(mfp_timer_t * const ptimer, int idx, bogoc68_t bogoc)
{
ptimer->def = timer_def[idx&3];
ptimer->tdr_res = 256;
ptimer->tdr_cur = ptimer->tdr_res;
reset_timer(ptimer, bogoc);
}
int mfp_setup(mfp_t * const mfp)
{
int ret = -1;
if (mfp) {
setup_timer(&mfp->timers[0], 0, 0);
setup_timer(&mfp->timers[1], 1, 0);
setup_timer(&mfp->timers[2], 2, 0);
setup_timer(&mfp->timers[3], 3, 0);
ret = mfp_reset_bogo(mfp, 0);
}
return ret;
}
void mfp_cleanup(mfp_t * const mfp)
{
}
int mfp_init(void)
{
if (mfp_cat == msg68_DEFAULT)
mfp_cat = msg68_cat("mfp","MFP-68901 emulator", DEBUG_MFP_O);
return 0;
}
void mfp_shutdown(void)
{
msg68_cat_free(mfp_cat);
mfp_cat = msg68_DEFAULT;
}