/*  ztimer.cpp
    Inspired by Michael Abrash's legendary Zen Timer.

    12.08.2001: tk, initial implementation.

    Copyright (c)2001 by Thomas Kindler, thomas.kindler@gmx.de

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of
    the License, or (at your option) any later version. Read the
    full License at http://www.gnu.org/copyleft for more details.
*/
#include <stdio.h>
#include <dos.h>
#include "amd186.h"

static unsigned old_t2con  = TCON_INT | TCON_CONT;
static unsigned old_t2cmpa = 5000;

static unsigned last_t2cnt;
static bool     isActive = false;

/**
 * Start the ZTimer. This function is to be called just before
 * the piece of code you want to measure. This function uses
 * Timer 2, which is otherwise used by the SC12 Bios. So better
 * don't call any of the BIOS functions, because it could confuse
 * it big time. All interrupts (except NMI) are disabled until
 * a call to ZTimer_Off.
 *
 * \warning
 * Using the ZTimer will affect the system time of the SC12, and
 * may cause all other sorts of instability. Keep in mind, that
 * the code to measure may not be longer than the watchdog refresh
 * interval. <b>Do not use for production code!</b>
 *
 * \bug
 * If you notice sporadic system restarts when using the ZTimer,
 * you should try to call the HAL_RefreshWatchdog() function, 
 * before using ZTimer_On(). 
 * Please send me a mail if this happens/helps.
 */
void ZTimer_Start()
{
  isActive = true;
  disable();
  old_t2con = inpw(PCB_T2CON);
  old_t2cmpa = inpw(PCB_T2CMPA);
  outpw(PCB_T2CON, TCON_STOP);
  outpw(PCB_T2CMPA, 0);
  outpw(PCB_T2CNT, 0);
  outpw(PCB_T2CON, TCON_START);
}


/**
 * Stop the ZTimer. This function has to be called right after
 * the piece of code to measure. Timer 2 is given back to the BIOS,
 * and interrupts are re-enabled.
 *
 * call ZTimer_Report() to show the measured time.
 *
 * \return  number of microseconds since ZTimer_Start()
 */
int ZTimer_Stop()
{
  last_t2cnt = inpw(PCB_T2CNT);
  outpw(PCB_T2CMPA, old_t2cmpa);
  outpw(PCB_T2CON, old_t2con | TCON_START);
  enable();
  isActive = false;
  return  last_t2cnt/5;
}


/**
 * Show measured time interval. This function prints out the
 * measured time in microseconds. Calling overhead for
 * ZTimer_On and ZTimer_Off is taken into account. (usually
 * 4-5 us on an @chipSC12)
 * \note  this function will call ZTimer_Stop(), if the
 *        ZTimer is still running.
 */
void ZTimer_Report()
{
  if (isActive)
    ZTimer_Stop();
  if (last_t2cnt) {
    // measure overhead
    //
    unsigned  time     = last_t2cnt;
    unsigned  overhead = 65535u;
    for (int i=0; i<16; i++) {
      ZTimer_Start();
      ZTimer_Stop();
      if (last_t2cnt < overhead)
        overhead = last_t2cnt;
    }
    printf("%u us\n", (time - overhead)/5);
  } else {
    printf(">13.1072 ms\n");
  }
}
