Skip to content. | Skip to navigation

Personal tools

lcu/sbcserck.cpp

lcu/sbcserck.cpp

lcu/sbcserck.cpp

/////////////////////////////////////////////////////////////////////////////
// SBCSERCK.CPP
//
//  Last updated: 3/16/2005
//
//  This file contains a clock interrupt driver, in addition to
//  polled serial functions.  It also provides more accurate
//  time than the CMOS clock with gettime and settime.
/////////////////////////////////////////////////////////////////////////////

#include <dos.h>
#include <conio.h>
#include <string.h>
#include <time.h>

// These routines are used to hook the system clock interrupt,
// typically at 1000Hz.  You can modify the interrupt routine to
// do basically anything at regular intervals.
//
// The current clock routines in this unit:
// InitClock(), which sets up the clock interrupt
// Delay(int Time), which waits for Time interrupts.
//   (if ClockRate = 1000, Delay() waits Time milliseconds)
// CloseClock(), which restores the clock interrupt to the system default

#define NumPorts 2 /* This routine can handle any number of COM ports */
#define BufferSize 1024 /* 1k Receive buffer for each serial port */

// 80C188EB Serial port register offsets
#define CMP  0
#define CNT  2
#define CON  4
#define STS  6
#define RBUF 8
#define TBUF 10

// 80C188EB Peripheral Control Block default base address after reset
#define PCB 0xFF00

// 80C188EB Interrupt controller registers
#define EOI   PCB+0x002
#define IMASK PCB+0x008

// 80C188EB Timer1 registers
#define T0CON  0xFF36
#define T1CNT  0xFF38
#define T1CMPA 0xFF3A
#define T1CMPB 0xFF3C
#define T1CON  0xFF3E

/////////////////////////////////////////////////////////////////////////////
// LOG OF CHANGES
// 02/10/05: Added ClockRate independent msTimer, and overrun counter
// 12/18/04: Adjusted general clock routines to SBC1190 CPU board.
// 06/06/02: Serial Port parameters setting up with InitComPort() function.
// 03/24/99: Better accuracy time-keeping, and time-keeping functions
// 04/22/98: Disabled port I/O if port not in use.
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Global Variables
int ClockRate;        // Clock rate in Hz
double RealClockRate; // A more accurate clock rate in Hz
volatile unsigned long NanoSecPerTick; // The number of nanoseconds per clock tick
volatile unsigned long Timer;
// Timer is a global variable for other programs to use... it keeps track
// of the number of clock interrupts that have occurred since InitClock
// was called. If the ClockRate is set at 1000Hz, Timer will be incremented
// by the interrupt driver every millisecond (pretty accurately).  This can
// be used to time events, watch for timeouts, etc.
volatile unsigned long HighTimer;
// HighTimer is used to expand Timer to 64 bits
//volatile unsigned long msTimer;
// This is similar to Timer, but is incremented every millisecond regardless
// of the actual ClockRate (obviously, ClockRate must be >= 1000 Hz for this
// to work every millisecond.. it will still be accurate for slower clock
// rates, but it will not be updated every millisecond)
//volatile unsigned long HighmsTimer;
// HighmsTimer is used to expand msTimer to 64 bits
//volatile unsigned long msCounter;
// msCounter is used to increment msTimer accurately
volatile int ClockReentry = 0;
// Reentry is a flag used to keep clock interrupts from colliding
volatile int Overruns = 0;
// Counts clock overruns.. useful to see if you're running the clock too fast
void interrupt (*OldClockInterrupt)();
// This is a pointer to the old system clock interrupt
void (*UserClockInterrupt)() = NULL;
// This is a pointer to a user-definable function that gets called on each
// clock interrupt.  To use it, just write a function such as
//   void MyClockInterrupt()
//   {
//     ..whatever you want..
//   }
//
// and then, issue the command
//      UserClockInterrupt = &MyClockInterrupt;

/////////////////////////////////////////////////////////////////////////////
// Definition of the Ports[] array of serial port information

// There is one ComPort structure for each physical port
volatile struct ComPort
{
  unsigned char RxBuffer[BufferSize]; /* Receive buffer (circular FIFO) */
  unsigned char TxBuffer[BufferSize]; /* Transmit buffer (circular FIFO) */
  int RxAdd,RxRem,RxBuf;
  // RxAdd, RxRem, and RxBuf are indices into the receive buffer.
  //      RxAdd = index to add new characters at.
  //      RxRem = index to read oldest stored characters.
  //      RxBuf = number of characters stored.
  int TxAdd,TxRem,TxBuf;
  // TxAdd, TxRem, and TxBuf are indices into the transmit buffer.
  //      TxAdd = index to add new characters at.
  //      TxRem = index to read oldest stored characters.
  //      TxBuf = number of characters stored.
  int RxOverruns,TxOverruns;
  // Keep track of buffer overruns for diagnostic use
  long int BaudRate;
  // Stores the baud rate we are running the serial port at.
  // The maximum value is 1/16 CPU clock.
  int PortAddress,Parity,EvenParity,InUse,Mode;
  // PortAddress: = physical address of port i.e. 0xFF60 for COMB/Port0, 0xFF70 for COMA/Port1
  // Parity: 0 = No parity (Normal), 1 = Parity enabled
  // EvenParity: 0 = Even parity (Normal), 1 = Odd parity
  // InUse: The serial driver is actively controlling this port
  // Mode: Operating mode for the serial port channel 0-4 (see manual)
} Ports[NumPorts]; // In the Ports[] array, 0 = COMB: and 1 = COMA:.

/////////////////////////////////////////////////////////////////////////////
// SerChan0Interrupt:  Disable/enable Serial Channel 0 interrupt requests
void SerChan0Interrupt(int en)
// en = 0 disables, en = 1 enables interrupt
{
  if (en)
    outpw(IMASK,(inpw(IMASK) & 0x00F9));
  else
    outpw(IMASK,(inpw(IMASK) | 0x0004));
}

/////////////////////////////////////////////////////////////////////////////
// TimersInterrupt:  Disable/enable interrupt requests from the timers
void TimersInterrupt(int en)
// en = 0 disables, en = 1 enables interrupt
{
  if (en)
    outpw(IMASK,(inpw(IMASK) & 0x00FC));
  else
    outpw(IMASK,(inpw(IMASK) | 0x0001));
}

/////////////////////////////////////////////////////////////////////////////
// MaskInterrupts: Disable/enable interrupt requests from Serial Channel 0
// and from the timers
void MaskInterrupts(int en)
// en = 0 disables, en = 1 enables interrupts
{
  SerChan0Interrupt(en);
  TimersInterrupt(en);
}

/////////////////////////////////////////////////////////////////////////////
// Power2: Function to calculate powers of 2
int Power2(int x)
// returns 2 raised to the xth power
{
  int Result = 1;
  while (x-- > 0) Result *= 2;
  return(Result);
}

/////////////////////////////////////////////////////////////////////////////
// ClockInterrupt: The clock interrupt routine
void interrupt ClockInterrupt()
// This is our new clock interrupt.  You can add code here to be executed
// every clock cycle.
{
  int Port;
  unsigned char SxSTS;

  if (!ClockReentry)
  {
    ClockReentry = 1;
    _enable(); // Re-enable interrupts after flagging to avoid reentrance

    // We're just counting the passage of milliseconds
    Timer++;
    // Update the upper 32 bits of Timer if necessary
    if (Timer == 0)
      HighTimer++;

    /*// Update the millisecond counter
    msCounter += NanoSecPerTick;

    // Update millisecond timer
    if (msCounter >= 1000000)
    {
      // Did msTimer wrap around?
      if ((msTimer + msCounter / 1000000) < msTimer)
        HighmsTimer++;
      msTimer += msCounter / 1000000;
      msCounter = msCounter % 1000000;
    }*/

    // Poll serial ports
    for (Port=0;Port<NumPorts;Port++)
    // Scan all serial ports for incoming bytes
      if (Ports[Port].InUse)
      // Only check a serial port if it has been flagged as being used
      {
        // Clear any error flags (we ignore them)
        //inp(Ports[Port].PortAddress + IID);
        //inp(Ports[Port].PortAddress + MSR);

        // While SxSTS & 0x40 = 1, a byte is ready to be read
        SxSTS = inp(Ports[Port].PortAddress + STS);
        while ((SxSTS & 0x40) > 0)
        {
          // Only add characters to the buffer if the buffer isn't full
          if (Ports[Port].RxBuf < BufferSize)
          {
            // Read the available byte and append it to our internal buffer
            Ports[Port].RxBuffer[Ports[Port].RxAdd] = inp(Ports[Port].PortAddress + RBUF);
            Ports[Port].RxBuf++;
            // Don't let the number of characters stored exceed the buffer size.
            // (We discard characters when the buffer overruns)
            if (Ports[Port].RxBuf >= BufferSize)
              Ports[Port].RxBuf = BufferSize - 1;
            Ports[Port].RxAdd++;
            // If RxAdd has passed the end of the array, wrap around
            if (Ports[Port].RxAdd >= BufferSize)
              Ports[Port].RxAdd = 0;
            SxSTS = inp(Ports[Port].PortAddress + STS);
          }
          // Otherwise discard incoming character
          else
          {
            Ports[Port].RxOverruns++;
            inp(Ports[Port].PortAddress + RBUF);
          }
        }

        // If there are bytes to transmit and SxSTS & 0x08 is set (UART is ready to Tx)...
        if (((SxSTS & 0x08) > 0) && (Ports[Port].TxBuf > 0))
        {
          // Transmit available bytes and remove from our buffer
          outp(Ports[Port].PortAddress + TBUF,Ports[Port].TxBuffer[Ports[Port].TxRem]);
          Ports[Port].TxRem++;
          if (Ports[Port].TxRem >= BufferSize)
            Ports[Port].TxRem = 0;
          Ports[Port].TxBuf--;
        }
      }

    // User code should go into this function
    if (UserClockInterrupt != NULL)
      (*UserClockInterrupt)();
    // End user code

    // Disable interrupts before clearing reentrance flag
    _disable();
    ClockReentry = 0;
  }
  else
  {
    // Count clock interrupt overruns for diagnostic purpouses
    Overruns++;
  }
  // Disable interrupts before ending this interrupt
  _disable();

//InitTimer
  outpw(EOI,0x0008);

}

/////////////////////////////////////////////////////////////////////////////
// InitClock: Function to initialize the clock interrupt driver
void InitClock(int clockRate)
// Initializes the clock interrupt to occur at clockRate cycles per second.
// Example: InitClock(1000) runs at 1000 Hz, and counts clock ticks in the
// Timer variable. RealClockRate will hold the actual clock rate
// (which should be close to the requested value)
{
  unsigned int NCR;
  // Run.exe divides CPU clock (through Timer2) down to 390.625KHz
  // for a 25MHz SBC1190 (25M/64 = 390625)
  // then Timer2 signal is fed into Timer0 and Timer1
  unsigned long ClockFactor = 390625;
  int i;

  // Mark all serial ports as unused
  for (i = 0;i < NumPorts;i++)
    Ports[i].InUse = 0;
  // Store requested clock rate
  ClockRate = clockRate;
  // Initialize our millisecond timer
  Timer = 0;
  HighTimer = 0;
  //msTimer = 0;
  //HighmsTimer = 0;

  // Save the old system clock interrupt vector
  OldClockInterrupt = _dos_getvect(0x12);
  // Disable interrupts while we play around
  _disable();
  /* disable timer 1 interrupt */
  TimersInterrupt(0);
  // Point the clock interrupt to our interrupt routine
  /* setup timer 1 interrupt vector */
  _dos_setvect(0x12,ClockInterrupt);

  // Tell the clock system that we are changing the clock interrupt rate

  // Clear Timer1 Count Reg
  outpw(T1CNT,0x0000);

  /* setup timer 1 counter */
  /* timer 2 is prescaler, output = 390.625kHz */
  /* 390.625KHz/390.625 = 1000 Hz or approx 1msec */
  // Set the new clock rate
  NCR = ClockFactor/ClockRate;
  outpw(T1CMPA,NCR);

  /* setup timer 1 operation mode*/
  // Enable, Inhibit on, Interrupt on, Reg In Use A,
  // Prescaler on, Single Max count mode, Continous count
  outpw(T1CON,0xE009);

  /* enable timer 1 interrupt */
  TimersInterrupt(1);

  /* disable timer 0 interrupt*/
  //outpw(T0CON,(inpw(T0CON) & 0xDFFF));

  RealClockRate = (float)ClockFactor/(ClockFactor/ClockRate);
  NanoSecPerTick = (double)1000000000.0 / RealClockRate;
  //msCounter = 0;
  // Re-enable interrupts.. our driver is installed
  _enable();
}

/////////////////////////////////////////////////////////////////////////////
// Delay: Function to wait for a specified number of clock ticks
void Delay(unsigned long Time)
// This routine simply waits for Time clock interrupts to pass.  If the
// Clockrate is at 1000Hz, this routine waits for Time milliseconds.
{
        unsigned long OldTime;

        OldTime = Timer;
        while ((Timer - OldTime) < Time);
}

/////////////////////////////////////////////////////////////////////////////
// CloseClock: Restore the clock interrupt to normal
void CloseClock()
// Restore the system clock interrupt to how we found it
{
  // Disable interrupts
  _disable();
  // Set the clock interrupt vector to its old value
  _dos_setvect(0x12,OldClockInterrupt);
  // Restore the clock interrupt rate to 18.2Hz
  //outp(0x43,0x36);
  //outp(0x40,0);
  //outp(0x40,0);
  /* Disable timer 1 operation*/
  outpw(T1CON,0x4000);
  // Re-enable interrupts
  _enable();
}

/////////////////////////////////////////////////////////////////////////////
// settime - Set the CMOS clock
void settime(struct dostime_t* t)
{
  _dos_settime(t);
}

/////////////////////////////////////////////////////////////////////////////
// SetNewClockPPM - Adjust clock rate by given parts-per-million
void SetNewClockPPM(int NewPPM)
{
  NanoSecPerTick = (double)1000000000.0 /
                  ((1.0 + (double)NewPPM * 0.000001) * RealClockRate);
}

/////////////////////////////////////////////////////////////////////////////
// ClearSerialBuffer: Function to clear a serial port's receive buffer
void ClearSerialBuffer(int Port)
{
  unsigned char SxSTS;

  if (Ports[Port].InUse)
  {
    _disable();
    // While SxSTS & 0x40 = 1, a byte is ready to be read
    SxSTS = inp(Ports[Port].PortAddress + STS);
    while ((SxSTS & 0x40) > 0)
    {
      inp(Ports[Port].PortAddress + RBUF);
      SxSTS = inp(Ports[Port].PortAddress + STS);
    }
    Ports[Port].RxAdd = 0;
    Ports[Port].RxBuf = 0;
    Ports[Port].RxRem = 0;
    _enable();
  }
}

/////////////////////////////////////////////////////////////////////////////
// SerialBufCharReady: Tells whether a character has been received by a serial port
int SerialBufCharReady(int Port)
// Returns non-zero value if a character is in the buffer,
// or a zero if the buffer is empty.
// Pass the com port you want to check in Port (0=COMB:,1=COMA:).
{
        return(Ports[Port].RxBuf);
}

/////////////////////////////////////////////////////////////////////////////
// SerialBufGetChar: Function to read a waiting serial character
unsigned char SerialBufGetChar(int Port)
// Returns a character from the serial buffer for port Port(0=COMB,etc).
// You can make sure a character is available in the buffer with
// SerialBufCharReady() before calling this function.
{
  unsigned char Data; // The character to be returned

  // Check to make sure a character is ready
  if (Ports[Port].RxBuf > 0)
  {
    _disable();
    Data = Ports[Port].RxBuffer[Ports[Port].RxRem];
    Ports[Port].RxBuf--;
    Ports[Port].RxRem++;
    if (Ports[Port].RxRem >= BufferSize)
      Ports[Port].RxRem = 0;
    _enable();
    return(Data);
  }
  return(0);
}

/////////////////////////////////////////////////////////////////////////////
// SerialBufReadyToSend: Function to see if transmit buffer is empty
int SerialBufReadyToSend(int Port)
// Returns a 0 if bytes are still in the buffer, 1 if buffer empty
{
  return(!Ports[Port].TxBuf);
}

/////////////////////////////////////////////////////////////////////////////
// SerialBufPutString: Function to buffer and eventually write a string
void SerialBufPutString(int Port,const char *Data)
{
  int i;

  _disable();
  for (i=0;i<strlen(Data);i++)
    // Only send data if the buffer isn't full
    if (Ports[Port].TxBuf < BufferSize)
    {
      // Read the available byte and append it to our internal buffer
      Ports[Port].TxBuffer[Ports[Port].TxAdd] = Data[i];
      Ports[Port].TxBuf++;
      // Don't let the number of characters stored exceed the buffer size.
      if (Ports[Port].TxBuf > BufferSize) Ports[Port].TxBuf = BufferSize;
        Ports[Port].TxAdd++;
      // If TxAdd has passed the end of the array, wrap around
      if (Ports[Port].TxAdd >= BufferSize) Ports[Port].TxAdd = 0;
    }
    // Record an overrun
    else
    {
      Ports[Port].TxOverruns++;
    }
  _enable();
}

/////////////////////////////////////////////////////////////////////////////
// SerialPutChar: Function to write a character to a serial port
void SerialPutChar(int Port,unsigned char Data)
// Output a character (in Data) to the port Port (0=ComB:,1=ComA:)
{
  unsigned char SxSTS;

  if (Ports[Port].InUse)
  {
    // Wait for the COM Port to be ready to transmit
    do
      SxSTS = inp(Ports[Port].PortAddress + STS);
    while ((SxSTS & 0x08) == 0);
    // Send the character
    outp(Ports[Port].PortAddress + TBUF,Data);
  }
}

/////////////////////////////////////////////////////////////////////////////
// SerialPutString: Function to write a string to a serial port
void SerialPutString(int Port,const char *Data)
// Output a string (in Data) to serial port Port (0=ComB:,1=ComA)
{
  int i;

  for (i=0;i<strlen(Data);i++)
    SerialPutChar(Port,Data[i]);
}

/////////////////////////////////////////////////////////////////////////////
// SerialGeChar: Function to read a character from a serial port
unsigned char SerialGetChar(int Port)
// Read a character from the port Port (0=ComB:,1=ComA:)
// returns character in Data
{
  unsigned char SxSTS;
  unsigned char Data; // The character to be returned

  if (Ports[Port].InUse)
  {
    // Wait for the COM Port to receive the byte
    do
      SxSTS = inp(Ports[Port].PortAddress + STS);
    while ((SxSTS & 0x40) == 0);
    // Read the character
    Data = inp(Ports[Port].PortAddress + RBUF);
    return(Data);
  }
  return(0);
}

/////////////////////////////////////////////////////////////////////////////
// SetComPortSpeed: Function to change the serial port's speed on the fly
void SetComPortSpeed(int Port,long BaudRate)
// Set a port's baud rate (Used in InitComPorts, and it can be used to
// change the baud rate on the fly. Port contains the port to set (0=COMB: or 1=COMA:),
// and BaudRate contains the requested baudrate (i.e. 19200).
{
  unsigned int BDiv;
  unsigned char Control = 0;

  // Disable interrupts while we change the baud rate
  _disable();

  //BxCMP = (CPUfreq/(BaudRate x 8))-1;
  // See 80C188EB processor manual
  BDiv = 25000000 / (BaudRate * 8) - 1;
  // Set the baud rate
  // Baud Rate Compare Register
  outpw(Ports[Port].PortAddress + CMP,0x8000 + BDiv);

  // Set up the Control byte for the various port statistics
  if      (Ports[Port].Mode == 0) Control |= 0x0000;
  else if (Ports[Port].Mode == 1) Control |= 0x0001;
  else if (Ports[Port].Mode == 2) Control |= 0x0002;
  else if (Ports[Port].Mode == 3) Control |= 0x0003;
  else if (Ports[Port].Mode == 4) Control |= 0x0004;
  if (Ports[Port].Parity)     Control |= 0x0008;
  if (Ports[Port].EvenParity) Control |= 0x0010;
  // RX machine always enabled.
  Control |= 0x0020;

  outpw(Ports[Port].PortAddress + CON,Control);

  // Re-enable interrupts
  _enable();
}

/////////////////////////////////////////////////////////////////////////////
// RestoreComPortSpeed: Function to set a serial port's speed back to the default
void RestoreComPortSpeed(int Port)
// Restore a port's baud rate to the default in the Port array
{
  SetComPortSpeed(Port,Ports[Port].BaudRate);
}

/////////////////////////////////////////////////////////////////////////////
// InitComPort: Function to initialize a serial port, and install the interrupt driver
void InitComPort(int Port,int PortAddress,long BaudRate,char* Config)
// This prepares a COM port for use, grabs the necesary interrupt
// vectors, and initializes all necessary variables.
//
// Pass the com port # to use in Port, (0 = COMB:,1 = COMA:)
// the port address in Port Address,   (0xFF60 = COMB:,0xFF70 = COMA:)
// the baud rate in BaudRate,          (ie 19200)
// the mode/parity configuration as a string in Config
//  (ex: "1N" = mode 1, no parity)
{

  Ports[Port].PortAddress = PortAddress;
  Ports[Port].BaudRate = BaudRate;
  if ((Config[0] >= '0') && (Config[0] <= '4'))
    Ports[Port].Mode = Config[0] - '0';
  else
    Ports[Port].Mode = 1;
  if ((Config[1] == 'O') || (Config[1] == 'o'))
  {
    Ports[Port].Parity = 1;
    Ports[Port].EvenParity = 0;
  }
  else if ((Config[1] == 'E') || (Config[1] == 'e'))
  {
    Ports[Port].Parity = 1;
    Ports[Port].EvenParity = 1;
  }
  else
    Ports[Port].Parity = 0;
  // Set the port speed and all control bits (parity, stop bits, etc)
  SetComPortSpeed(Port,Ports[Port].BaudRate);
  _disable();
  Ports[Port].InUse = 1;
  // Reset receive buffer
  Ports[Port].RxAdd = 0;
  Ports[Port].RxRem = 0;
  Ports[Port].RxBuf = 0;
  // Reset transmit buffer
  Ports[Port].TxAdd = 0;
  Ports[Port].TxRem = 0;
  Ports[Port].TxBuf = 0;
  // Reset overruns
  Ports[Port].RxOverruns = 0;
  Ports[Port].TxOverruns = 0;
  // Clear all read ports
  inpw(Ports[Port].PortAddress + STS);
  inpw(Ports[Port].PortAddress + RBUF);
  //inp(Ports[Port].PortAddress + TRX);
  //inp(Ports[Port].PortAddress + LSR);
  //inp(Ports[Port].PortAddress + MSR);
  //inp(Ports[Port].PortAddress + IID);
  // Set modem control bits
  //outp(Ports[Port].PortAddress + MCR,0x03);
  // Disable serial interrupts
  //outp(Ports[Port].PortAddress + IEN,0x00);
  SerChan0Interrupt(1);
  // Re-enable interrupts
  _enable();
}

/////////////////////////////////////////////////////////////////////////////
// CloseComPorts: Function to restore all interrupt vectors
void CloseComPorts()
// Restore all interrupt vectors to their original values
{
  _disable();
  for (int Port = 0;Port < NumPorts;Port++)
    if (Ports[Port].InUse)
    {
      //outp(Ports[Port].PortAddress+MCR,0);
      //outp(Ports[Port].PortAddress+IEN,0);
      //outp(Ports[Port].PortAddress+IID,0);
      Ports[Port].InUse = 0;
    }
  _enable();
}

Generated by GNU Enscript 1.6.5.2.
Document Actions