// =====================================================
// Работа с асинхронным адаптером COM1.
// Перед запуском программы необходимо замкнуть
// контакты 2 и 3 разъема COM1
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================

#include <stdio.h>
#include <conio.h>

typedef struct _AUX_MODE_ 
{
  union 
  {
    struct 
    {
      unsigned char len : 2, // длина символа
           stop         : 1, // число стоп-битов
           parity       : 2, // контроль четности
           stuck_parity : 1, // фиксация четности
           en_break_ctl : 1, // установка перерыва
           dlab         : 1; // загрузка регистра делителя
    } ctl_word;
    char ctl;
  } ctl_aux;
  unsigned long baud; // скорость передачи данных
} AUX_MODE;

void aux_stat(AUX_MODE *mode, int port);
int aux_init(AUX_MODE *mode, int port, int imask);
void aux_outp(char chr, int port);
char aux_inp(int port);

int main(void) 
{
  AUX_MODE amd;

  aux_stat(&amd, 0);
  printf("\nСостояние порта COM1:"
    "\nКод длины символа:    %d"
    "\nКод числа стоп-битов: %d"
    "\nКонтроль четности:    %d"
    "\nСкорость передачи:    %lu",
    amd.ctl_aux.ctl_word.len,
    amd.ctl_aux.ctl_word.stop,
    amd.ctl_aux.ctl_word.parity,
    (unsigned long)amd.baud);

  amd.baud = 115200;
  aux_init(&amd, 0, 0);

  aux_stat(&amd, 0);
  printf("\nСостояние порта COM1:"
    "\nКод длины символа:    %d"
    "\nКод числа стоп-битов: %d"
    "\nКонтроль четности:    %d"
    "\nСкорость передачи:    %lu",
    amd.ctl_aux.ctl_word.len,
    amd.ctl_aux.ctl_word.stop,
    amd.ctl_aux.ctl_word.parity,
    (unsigned long)amd.baud);

  printf("\n\nТестирование асинхронного адаптера."
    "\nНажимайте клавиши!"
    "\nДля завершения работы нажмите <Contril+C>\n");

  for(;;) 
  {
    // Вводим символ с клавиатуры и передаем его
    // в асинхронный адаптер
    aux_outp((char)getch(), 0);

    // Вводим символ из асинхронного адаптера и
    // отображаем его на экране
    putchar(aux_inp(0));
  }
  return 0;
}

/**
*.Name         aux_stat
*.Title        Определение режима асинхронного адаптера
*
*.Descr        Эта функция считывает текущий режим
*              асинхронного порта и записывает его
*              в структуру с типом AUX_MODE
*
*.Proto        void aux_stat(AUX_MODE *mode, int port);
*
*.Params       AUX_MODE mode - структура, описывающая
*              протокол и режим работы порта:
*
*              int port - номер асинхронного адаптера:
*                 0 - COM1, 1 - COM2
**/
void aux_stat(AUX_MODE *mode, int port) 
{
  unsigned long b;

  // Запоминаем режим адаптера
  mode->ctl_aux.ctl = (char)inp(0x3fb - 0x100 * port);

  // Устанавливаем старший бит режима
  // для считывания текушей скорости передачи
  outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl | 0x80);

  // Считываем значение регистра делителя
  b = inp(0x3f9 - 0x100 * port); b = b << 8;
  b += inp(0x3f8 - 0x100 * port);

  // Преобразуем его в боды
  switch (b) 
  {
    case 1040: b = 110; break;
    case 768: b = 150; break;
    case 384: b = 300; break;
    case 192: b = 600; break;
    case 96: b = 1200; break;
    case 48: b = 2400; break;
    case 24: b = 4800; break;
    case 12: b = 9600; break;
    case 6: b = 19200; break;
    case 3: b = 38400; break;
    case 2: b = 57600; break;
    case 1: b = 115200; break;
    default: b=0; break;
  }

  mode->baud = b;

  // Восстанавливаем состояние адаптера
  outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl & 0x7f);
}

/**
*.Name         aux_init
*.Title        Инициализация асинхронного адаптера
*
*.Descr        Эта функция инициализирует асинхронные
*              адаптеры, задавая протокол обмена данными
*              и скорость обмена данными
*
*.Proto        int aux_init(AUX_MODE *mode, int port,
*                                        int imask);
*
*.Params       AUX_MODE *mode - указатель на структуру,
*                               описывающую протокол и режим работы 
*                               порта;
*
*              int port - номер асинхронного адаптера:
*                 0 - COM1, 1 - COM2
*
*              int imask - значение для регистра маски
*                          прерываний
*
*.Return       0 - инициализация выполнена успешно;
*              1 - ошибки в параметрах инициализации.
**/
int aux_init(AUX_MODE *mode, int port, int imask) 
{
  unsigned div;
  char ctl;

  // Вычисляем значение для делителя
  switch (mode->baud) 
  {
    case 110: div = 1040; break;
    case 150: div = 768; break;
    case 300: div = 384; break;
    case 600: div = 192; break;
    case 1200: div = 96; break;
    case 2400: div = 48; break;
    case 4800: div = 24; break;
    case 9600: div = 12; break;
    case 19200: div = 6; break;
    case 38400: div = 3; break;
    case 57600: div = 2; break;
    case 115200: div =1; break;
    default: 
      return(-1); break;
  }

  // Записываем значение делителя частоты
  ctl = inp(0x3fb - 0x100 * port);
  outp(0x3fb - 0x100 * port, ctl | 0x80);

  outp(0x3f9 - 0x100 * port, (div >> 8) & 0x00ff);
  outp(0x3f8 - 0x100 * port, div & 0x00ff);

  // Записываем новое управляющее слово
  outp(0x3fb - 0x100 * port, mode->ctl_aux.ctl & 0x7f);

  // Устанавливаем регистр управления прерыванием
  outp(0x3f9 - 0x100 * port, imask);
  
  return 0;
}

/**
*.Name         aux_outp
*.Title        Вывод символа в асинхронный адаптер
*
*.Descr        Эта функция дожидается готовности
*              передатчика и посылает символ
*
*.Proto        void aux_outp(char chr, int port);
*
*.Params       char chr - посылаемый символ;
*
*              int port - номер асинхронного адаптера:
*                 0 - COM1, 1 - COM2
**/
void aux_outp(char chr, int port) 
{
  unsigned status_reg, out_reg;

  status_reg = 0x3fd - 0x100 * port;
  out_reg = status_reg - 5;

  while( (inp(status_reg) & 0x20) == 0 );
  outp(out_reg, chr);
}

/**
*.Name         aux_inp
*.Title        Ввод символа из асинхронного адаптера
*
*.Descr        Эта функция дожидается готовности
*              приемника и вводит символ из асинхронного
*              адаптера
*
*.Proto        char aux_inp(int port);
*
*.Params       int port - номер асинхронного адаптера:
*                 0 - COM1, 1 - COM2
*
*.Return       Принятый символ
**/
char aux_inp(int port) 
{
  unsigned status_reg, inp_reg;

  status_reg = 0x3fd - 0x100 * port;
  inp_reg = status_reg - 5;

  while( (inp(status_reg) & 1) == 0 );
  return(inp(inp_reg));
}
