/* Write Timer Data Register: Changing the value of the TDR change the value of the internal reset latch. However when the timer is stopped the reset value is reload into TDR in the next MFP cycle (so it seems to be the case according to my test on a real ST). The tricky point is that as MFP and CPU clock are separate (for Atari-ST) reading the value just after a write can occur before the next MFP cycle in which case the current TDR (not the one written) is returned! Fortunatly we don't really need to emulate this since it is more a glitch than anything a no one should have used it! Motorola Datasheet: the TDRs contain the value of their respective main counter. This value was captured on the last low-to-high transistion of the data strobe pin. The main counter is initialized by writting the to the TDR. If the timer is stopped the data is loaded simultaneously into both TDR and maincounter. If the TDR is written to while the timer is enabled, the value is not loaded into the timer until the timer counts through 01. If a write is performed while the timer is counting through 01 then an *INDETERMINATE* value is loaded into the main counter. */ void mfp_put_tdr(mfp_t * const mfp, int timer, int v, bogoc68_t bogoc) { mfp_timer_t * const ptimer = &mfp->timers[timer&3]; const uint68_t old_tdr = ptimer->tdr_res; /* Interrupt when count down to 0 so 0 is 256 */ v = (u8)v; v += (!v)<<8; ptimer->tdr_res = v; if (!ptimer->tcr) { ptimer->tdr_cur = v; if (mfp_feature) fprintf(stderr, "Reload timer-%c TDR @%u => %u\n", ptimer->def.letter, bogoc, ptimer->tdr_res); } else if (ptimer->tcr && v != old_tdr) { uint68_t old_frq = timerfrq(old_tdr); if (mfp_feature) fprintf(stderr, "Change timer-%c @%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), old_frq,old_tdr, timerfrq(ptimer->tdr_res), ptimer->tdr_res); } }
/* Write Timer Data Register: Changing the value of the TDR change the value of the internal reset latch. However when the timer is stopped the reset value is reload into TDR in the next MFP cycle (so it seems to be the case according to my test on a real ST). The tricky point is that as MFP and CPU clock are separate (for Atari-ST) reading the value just after a write can occur before the next MFP cycle in which case the current TDR (not the one written) is returned! Fortunatly we don't really need to emulate this since it is more a glitch than anything and no one should have used it! Motorola Datasheet: the TDRs contain the value of their respective main counter. This value was captured on the last low-to-high transistion of the data strobe pin. The main counter is initialized by writting the to the TDR. If the timer is stopped the data is loaded simultaneously into both TDR and maincounter. If the TDR is written to while the timer is enabled, the value is not loaded into the timer until the timer counts through 01. If a write is performed while the timer is counting through 01 then an INDETERMINATE value is loaded into the main counter. */ void mfp_put_tdr(mfp_t * const mfp, int timer, int68_t v, const bogoc68_t bogoc) { mfp_timer_t * const ptimer = &mfp->timers[timer&3]; #ifndef NDEBUG const uint_t old_tdr = ptimer->tdr_res; #endif /* Interrupt when count down to 0 so 0 is 256 */ v = (u8)v; v += (!v)<<8; ptimer->tdr_res = v; if (!ptimer->tcr) { ptimer->tdr_cur = v; TRACE68(mfp_cat, MYHD "timer-%c -- reload TDR @%u -- %u\n", ptimer->def.letter, (unsigned) bogoc, (unsigned) ptimer->tdr_res); } #ifndef NDEBUG else if (ptimer->tcr && v != old_tdr) { uint_t old_frq = timerfrq(old_tdr); TRACE68(mfp_cat, MYHD "timer-%c -- change @%u cti:%u psw:%u(%u) cpp:%u" " -- %u(%u) -> %u(%u)hz\n", ptimer->def.letter, (unsigned) bogoc, (unsigned) ptimer->cti, (unsigned) prediv_width[ptimer->tcr], (unsigned) ptimer->tcr, (unsigned) cpp(ptimer->tdr_res), (unsigned) old_frq, (unsigned) old_tdr, (unsigned) timerfrq(ptimer->tdr_res), (unsigned) ptimer->tdr_res); } #endif }
/* Control register changes, adjust ``cti'' (cycle to next interrupt) * * This case is a bit tricky : Changing timer prescale on the fly * may have unpredictable result mostly because we dunno how * prescaler works exactly. Here I assume the prescaler is * resetted. * * !!! chipmon of synergy does !!! * */ static inline void reconf_timer(mfp_timer_t * const ptimer, int tcr, const bogoc68_t bogoc) { #if !defined(NDEBUG) || !defined(CPP_SUPPORTS_VA_MACROS) uint_t frq = timerfrq(ptimer->tdr_res); /* old frequency */ #endif const bogoc68_t cti = ptimer->cti - bogoc; /* cycles to interrupt */ const uint_t psw = prediv_width[ptimer->tcr]; /* cycles count-down */ const uint_t cnt = cti/psw; /* count-down */ const uint_t psr = cti % psw; /* const uint68_t psc = psw-psr; */ /* cnt%ptimer->tdr_res+1; no MODULO since TDR may have change and anyway cti was calculated with 1 timer cycle !!! */ const uint_t tdr = cnt+1; const cycle68_t new_psw = prediv_width[(int)tcr]; if (bogoc > ptimer->cti) { TRACE68(mfp_cat, MYHD "timer-%c -- reconf out of range -- @%u > cti:%u\n", ptimer->def.letter, (unsigned) bogoc, (unsigned) ptimer->cti); ptimer->cti = bogoc + psw * ptimer->tdr_res; } else { ptimer->cti = bogoc + psr + (tdr-1) * new_psw; ptimer->cti = bogoc + /* psr + */ (tdr/* -1 */) * new_psw; } ptimer->tcr = tcr; TRACE68(mfp_cat, MYHD "timer-%c -- reconf @%u cti:%u cpp:%u -- %u:%uhz\n", ptimer->def.letter, (unsigned) bogoc, (unsigned) ptimer->cti, (unsigned) cpp(ptimer->tdr_res), (unsigned) frq, (unsigned) timerfrq(ptimer->tdr_res)); }
/* Control register changes, adjust ``cti'' (cycle to next interrupt) * * This case is a bit tricky : Changing timer prescale on the fly * may have unpredictable result mostly because we dunno how * prescaler works exactly. Here I assume the prescaler is * resetted. * * !!! chipmon of synergy does !!! * */ static __inline void reconf_timer(mfp_timer_t * const ptimer, int tcr, const bogoc68_t bogoc) { uint68_t frq = timerfrq(ptimer->tdr_res); /* old frequency */ const bogoc68_t cti = ptimer->cti - bogoc; /* cycles to interrupt */ const uint68_t psw = prediv_width[ptimer->tcr]; /* cycles count-down */ const uint68_t cnt = cti/psw; /* count-down */ const uint68_t psr = cti % psw; const uint68_t psc = psw-psr; /* cnt%ptimer->tdr_res+1; no MODULO since TDR may have change and anyway cti was calculated with 1 timer cycle !!! */ const uint68_t tdr = cnt+1; const cycle68_t new_psw = prediv_width[(int)tcr]; if (bogoc > ptimer->cti) { if (mfp_feature) fprintf(stderr, "Reconf timer-%c @%u > cti:%u !!!CYCLE OUT OF RANGE!!!\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 + /* psr + */ (tdr/* -1 */) * new_psw; /* if (ptimer->cti != cti_verif) { */ /* if (mfp_feature) fprintf(stderr, */ /* "Reconf timer-%c @%u psw:%u->%u psc:%u cti:%u!=%u !!!\n", */ /* ptimer->def.letter, bogoc, psw, new_psw, psc, */ /* cti_verif, ptimer->cti); */ /* } */ } ptimer->tcr = tcr; if (mfp_feature) fprintf(stderr, "Reconf timer-%c @%u cti:%u cpp:%u=> %d->%dhz\n", ptimer->def.letter, bogoc, ptimer->cti, cpp(ptimer->tdr_res), frq,timerfrq(ptimer->tdr_res)); }
/* Resume a stopped timer: tcr 0->!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; if (mfp_feature) fprintf(stderr, "Resume timer-%c @%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)); }
/* Resume a stopped timer: tcr 0->!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, MYHD "timer-%c -- resume @%u cti:%u cpp:%u " "tdr:%u/%u psw:%u(%u) -- %uhz\n", ptimer->def.letter, (unsigned) bogoc, (unsigned) ptimer->cti, (unsigned) cpp(ptimer->tdr_res), (unsigned) ptimer->tdr_cur, (unsigned) ptimer->tdr_res, (unsigned) prediv_width[ptimer->tcr], (unsigned) ptimer->tcr, (unsigned) timerfrq(ptimer->tdr_res)); }