//============================================================================================
//      tc_port_io.c          ver 2.2   (7/22/08)
//
//      copyright (c) 2001-2008 Vaux Electronics, Inc.
//============================================================================================


//============================================================================================
//
//  Public routine send_PA_out_char(c) allows main loop to send char to PA port buffer.
//  If buffer is full, we update diag_code, and discard char.
//
//  Public routine c = get_PA_in_char() allows main loop to get next char (if any)
//  from PA port buffer.  Returns 0 if the buffer is empty.
//
//  Public routine send_PB_out_char(c) allows main loop to send char to PB port buffer.
//  If buffer is full, we update diag_code, and discard char.
//
//  Public routine c = get_PB_in_char() allows main loop to get next char (if any)
//  from PB port buffer.  Returns 0 if the buffer is empty.
//
//  Private routine output_PA_bit(PA_bit) allows T1_isr to copy PA_bit value to TTY-Port-A pin
//  designated by global PA_out_bit.  Global TTY_port_image is updated as well.
//  When tty-port-A is configured as the TU port, tu_data_inversion is allowed.
//
//  Private routine output_PB_bit(PB_bit) allows T1_isr to copy PB_bit value to TTY-Port-B pin
//  designated by global PB_out_bit.  Global TTY_port_image is updated as well.
//  When tty-port-B is configured as the TU port, tu_data_inversion is allowed.
//
//  Timer-1 is a 125-us interrupt handler that provides software uarts for the TTY-Port-A
//  and TTY-Port-B ports.  Each port is toggled in succession, so the port sample rate is 250us.
//  Each port has 250-byte buffers on input streams (and 250-byte on output), with error codes loaded
//  into global diag_code if the buffers overflow (chars are lost on overflow).
//  Main can send error message, if desired.
//  Assignment of a specific physical port to TTY-Port-A or TTY-Port-B is done in main -- eg:
//            PA_in_bit =  TU_IN_BIT;                        // input pin for TU
//            PA_out_bit = TU_OUT_BIT;                       // output pin for TU
//            PA_bitrate = TU_bitrate - 1;                   // set to 1 less, for uart logic
//            PA_numbits = TU_numbits;                       // 5, 6, 7, or 8 bits
//  Option: when either tty-port-A or tty-port-B is configured as the TU port, tu_data_inversion is allowed.
//  Option: when tty-port-A input is idle, send LTRS char if tx_diddle_insertion is enabled.
//
//      In main prog:
//
//          #DEFINE         LV_MOT        PIN_A0        // LV loop motor (1 = on)
//          #DEFINE         HV1_MOT       PIN_A1        // HV1 loop motor (1 = on)
//          #DEFINE         HV2_MOT       PIN_A2        // HV2 loop motor (1 = on)
//          #DEFINE         PTT           PIN_A5        // Push-To-Talk output (on TU-232 jack) (1 = on)
//          ...
//          #USE            STANDARD_IO (A)
//          ...
//          #DEFINE         HV1_IN_BIT          0x01    // In (loop-sense) from HV1 loop
//          #DEFINE         HV1_OUT_BIT         0x02    // Out (loop-drive) to HV1 loop
//          #DEFINE         TU_IN_BIT           0x04    // In from TU-232 port (txd)
//          #DEFINE         TU_OUT_BIT          0x08    // Out to TU-232 port (rxd)
//          #DEFINE         LV_IN_BIT           0x10    // In (loop-sense) from LV loop
//          #DEFINE         LV_OUT_BIT          0x20    // Out (loop-drive) to LV loop
//          #DEFINE         HV2_IN_BIT          0x40    // In (loop-sense) from HV2 loop
//          #DEFINE         HV2_OUT_BIT         0x80    // Out (loop-drive) to HV2 loop
//
//          // can't use standard_io, since isr does input_b()
//          #USE            FIXED_IO (B_OUTPUTS=PIN_B1, PIN_B3, PIN_B5, PIN_B7)
//          ...
//          #DEFINE         T1_INT_MON    PIN_C2        // optional: monitors time in timer-1 isr
//          #DEFINE         SCI_TX        PIN_C6        // SCI: TX line
//          #DEFINE         SCI_RX        PIN_C7        // SCI: RX line
//          #USE            RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, BITS=8, PARITY=N, ERRORS)
//          ...
//          #USE            STANDARD_IO (C)
//          ...
//          #PRIORITY       TIMER1, TIMER2, RDA, TBE    // give timer1 high priority
//          ...
//          #INCLUDE        "tc_sci_isr.c"              // interrupt routines for sci (usart) i/o
//          #INCLUDE        "tc_port_io.c"              // routines for tty port send/receive, and software uarts (& timer-1 intr)
//          #INCLUDE        "tc_subs.c"                 // routines for translating baudot to/from ascii-67...
//          ...
//          void main()
//          {
//            ...
//            set_tris_A (0x00);                        // init pin directions (1=input=default, 0=output)
//            PORT_A = 0x00;                            // init output pins
//            ...
//            set_tris_B (0x55);                        // init directions (1=input=default, 0=output)
//            PORT_B = 0xAA;                            // init output pins (tty out ports all to mark)
//            ...
//            #IFDEF T1_INT_MON
//              output_low (T1_INT_MON);                // init TIMER-1 isr monitor pin
//            #ENDIF
//            use_XONXOFF_flg = TRUE;                   // sci_int: set to enable SW handshaking
//            ...
//            setup_counters (RTCC_INTERNAL, WDT_18MS);
//            setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
//            set_timer1 (T1RELOAD);                   // T1RELOAD defined in tc_port_io.c
//            enable_interrupts (INT_TIMER1);
//            enable_interrupts (INT_RDA);
//            enable_interrupts (GLOBAL);
//            ...
//            // ----- config TU port params  (change as needed):
//            TU_bitrate = BAUDOT_60_WPM;                    // TU port speed
//            TU_numbits = 5;                                // 5 bits for baudot
//            ...
//            // ----- config TTY-Port-A for TU use (change as needed for HV1...):
//            PA_in_bit =  TU_IN_BIT;
//            PA_out_bit = TU_OUT_BIT;
//            PA_bitrate = TU_bitrate - 1;                   // set to 1 less, for uart logic
//            PA_numbits = TU_numbits;                       // 5 bits for baudot
//            ...
//            while(1)                                       //  Main Event Loop
//            {
//              // ----- check for output to TTY-Port-A:
//              if (PA_out_buf_count == 0)                  // wait until buffer empties before adding any more chars
//              {
//                c = get_sci_rx_byte();                    // get pc port char, if avail  (stream is throttled with xon/xoff)
//                if (c)                                    // if char avail, send to tty port
//                {
//                  if (PA_numbits == 5)                    // if port is baudot, convert ASCII-67 to USTTY (baudot)
//                  {
//                    c = ascii_to_ustty (c);               // convert char
//                    if (baudot_out_new_case)              // if baudot_out_new_case is non-zero,
//                      send_PA_out_char(baudot_out_new_case);    //   then send change case (LTRS/FIGS) to tty port
//                  }
//                  send_PA_out_char(c);                    // send char to tty port
//                 }
//              }
//              ...
//              // ----- check for input from TTY-Port-A:
//              c = get_PA_in_char();                   // get tty port char, if avail
//              if (c)                                  // if char avail, send to pc port
//              {
//                if (PA_numbits == 5)                  // if port is baudot, convert USTTY (baudot) to ASCII-67
//                  c = ustty_to_ascii (c);             // convert char
//                send_sci_tx_byte(c);                  // send char to pc port
//              }
//              ...
//            }
//          }
//
//============================================================================================
//
//      to do:
//          -
//
//      tc_port_io.c          ver 2.2   (7/22/08)
//          - increased tty port buffers (Atx, Arx, Btx, Brx) to 250-bytes each (were 150 bytes)
//                (will need to change all pointers to int16 if we go past 255)
//
//      tc_port_io.c          ver 2.1   (7/xx/07)
//          - moved timer-2 code to tc_clock.c
//
//      tc_port_io.c          ver 2.0   (6/20/06)
//          - increased tty port buffers (Atx, Arx, Btx, Brx) to 150-bytes each (were 16 bytes)
//          - replaced byte (was typedef) with int8
//
//      tc_port_io.c          ver 1.0   (9/8/04)
//          - created from tty.c (from tty232 project)
//          - changed T1RELOAD reload value for 20MHz clock and 125 us int
//          - changed from single tty port to two:  PA and PB
//          - tty port assignment can now be changed from main
//          - changed other stuff too
//          - moved ascii/baudot conversion stuff to tty_subs.c
//
//============================================================================================


//  ----- prototypes:
void send_PA_out_char(char c);                      // send char to PA port buffer
char get_PA_in_char(void);                          // get next char (if any) from PA port buffer
void send_PB_out_char(char c);                      // send char to PB port buffer
char get_PB_in_char(void);                          // get next char (if any) from PB port buffer

void output_PA_bit(int1 PA_bit);                    // copy PA_bit value to TTY-Port-A pin
void output_PB_bit(int1 PB_bit);                    // copy PB_bit value to TTY-Port-B pin

void T1_isr(void);                                  // timer-1 intr is called every 125-us


// ----- values for PA_bitrate and PB_bitrate:
//          (these are defined as number of delay counts of 250 us interrupts for bit time)
//          (note: baudot is 5 data and 1.42 stop bits, ascii is 8 data and 2 stop bits)
//
//          >>> when loaded in main from in global such as TU_bitrate, to port param such as PA_bitrate,
//                actually set these to 1 count less, to work with logic in intr routine.
//                eg:  13.5 ms bits would be 54 counts per bit (13.5ms/250us), but gets loaded as 53
//                        TU_bitrate = BAUDOT_100_WPM;
//                        PA_bitrate = TU_bitrate - 1;

#define       BAUDOT_60_WPM           88            // 60 wpm = 45.45 baud = 22.0 ms bits (368 opm)
#define       BAUDOT_66_WPM           80            // 66 wpm = 50.00 baud = 20.0 ms bits (? opm)
#define       BAUDOT_75_WPM           72            // 75 wpm = 55.55 baud = 18.0 ms bits (460 opm)
#define       BAUDOT_100_WPM          54            // 100 wpm = 74.07 baud = 13.5 ms bits (600 opm)
#define       ASCII_110_BAUD          36            // 110 baud = 9.09 ms bits (100 wpm, 600 opm)
#define       ASCII_300_BAUD          13            // 300 baud = 3.33 ms bits


// ----- states for PA_in_state, PA_out_state, PB_in_state, and PB_out_state:
#define       TTY_IDLE                0
#define       TTY_START               1
#define       TTY_BIT_0               10
#define       TTY_BIT_1               11
#define       TTY_BIT_2               12
#define       TTY_BIT_3               13
#define       TTY_BIT_4               14
#define       TTY_BIT_5               15
#define       TTY_BIT_6               16
#define       TTY_BIT_7               17
#define       TTY_STOP                20
#define       TTY_2STOP               21
#define       TTY_TXEND               86


// ----- tty port buffer sizes:
#define       PA_OUTBUF_SIZE          250
#define       PA_INBUF_SIZE           250
#define       PB_OUTBUF_SIZE          250
#define       PB_INBUF_SIZE           250


// ----- globals:
static int8   T1_isr_index = 0;                     // toggles 0 to 1 in isr, to select current tty port
static int1   T1_bit;                               // bit var for isr use


int8          PA_out_buf[PA_OUTBUF_SIZE];           // PA_out_buf is circular out buffer
int8          *PA_out_buf_wr = PA_out_buf;          // write pointer for PA_out_buf
int8          *PA_out_buf_rd = PA_out_buf;          // read pointer for PA_out_buf
int8          PA_out_buf_count = 0;                 // number of chars in the PA_out_buf
int8          PA_in_buf[PA_INBUF_SIZE];             // PA_in_buf is circular in buffer for sci
int8          *PA_in_buf_wr = PA_in_buf;            // write pointer for PA_in_buf
int8          *PA_in_buf_rd = PA_in_buf;            // read pointer for PA_in_buf
int8          PA_in_buf_count = 0;                  // number of chars in the PA_in_buf

static int8   PA_in_bit = TU_IN_BIT;                // set by main as needed
static int8   PA_out_bit = TU_OUT_BIT;              // set by main as needed
static int8   PA_bitrate = BAUDOT_60_WPM - 1;       // set by main as needed (set less 1 for uart logic)
static int8   PA_numbits = 5;                       // set by main as needed (5 to 8)

static int8   PA_out_state = TTY_IDLE;              // software uart tx state
static int8   PA_out_bit_delay = 0;                 // software uart tx delay counter
static int8   PA_out_char;                          // software uart tx char
#bit          PA_out_char_b0 = PA_out_char.0
#bit          PA_out_char_b1 = PA_out_char.1
#bit          PA_out_char_b2 = PA_out_char.2
#bit          PA_out_char_b3 = PA_out_char.3
#bit          PA_out_char_b4 = PA_out_char.4
#bit          PA_out_char_b5 = PA_out_char.5
#bit          PA_out_char_b6 = PA_out_char.6
#bit          PA_out_char_b7 = PA_out_char.7

static int8   PA_in_state = TTY_IDLE;               // software uart rx state
static int8   PA_in_bit_delay = 0;                  // software uart rx delay counter
static int8   PA_in_char;                           // software uart rx char
#bit          PA_in_char_b0 = PA_in_char.0
#bit          PA_in_char_b1 = PA_in_char.1
#bit          PA_in_char_b2 = PA_in_char.2
#bit          PA_in_char_b3 = PA_in_char.3
#bit          PA_in_char_b4 = PA_in_char.4
#bit          PA_in_char_b5 = PA_in_char.5
#bit          PA_in_char_b6 = PA_in_char.6
#bit          PA_in_char_b7 = PA_in_char.7

char          PB_out_buf[PB_OUTBUF_SIZE];           // PB_out_buf is circular out buffer
char          *PB_out_buf_wr = PB_out_buf;          // write pointer for PB_out_buf
char          *PB_out_buf_rd = PB_out_buf;          // read pointer for PB_out_buf
char          PB_out_buf_count = 0;                 // number of chars in the PB_out_buf
char          PB_in_buf[PB_INBUF_SIZE];             // PB_in_buf is circular in buffer for sci
char          *PB_in_buf_wr = PB_in_buf;            // write pointer for PB_in_buf
char          *PB_in_buf_rd = PB_in_buf;            // read pointer for PB_in_buf
char          PB_in_buf_count = 0;                  // number of chars in the PB_in_buf

static int8   PB_in_bit = HV1_IN_BIT;               // set by main as needed
static int8   PB_out_bit = HV1_OUT_BIT;             // set by main as needed
static int8   PB_bitrate = BAUDOT_60_WPM - 1;       // set by main as needed (set less 1 for uart logic)
static int8   PB_numbits = 5;                       // set by main as needed (5 to 8)

static int8   PB_out_state = TTY_IDLE;              // software uart tx state
static int8   PB_out_bit_delay = 0;                 // software uart tx delay counter
static int8   PB_out_char;                          // software uart tx char
#bit          PB_out_char_b0 = PB_out_char.0
#bit          PB_out_char_b1 = PB_out_char.1
#bit          PB_out_char_b2 = PB_out_char.2
#bit          PB_out_char_b3 = PB_out_char.3
#bit          PB_out_char_b4 = PB_out_char.4
#bit          PB_out_char_b5 = PB_out_char.5
#bit          PB_out_char_b6 = PB_out_char.6
#bit          PB_out_char_b7 = PB_out_char.7

static int8   PB_in_state = TTY_IDLE;               // software uart rx state
static int8   PB_in_bit_delay = 0;                  // software uart rx delay counter
static int8   PB_in_char;                           // software uart rx char
#bit          PB_in_char_b0 = PB_in_char.0
#bit          PB_in_char_b1 = PB_in_char.1
#bit          PB_in_char_b2 = PB_in_char.2
#bit          PB_in_char_b3 = PB_in_char.3
#bit          PB_in_char_b4 = PB_in_char.4
#bit          PB_in_char_b5 = PB_in_char.5
#bit          PB_in_char_b6 = PB_in_char.6
#bit          PB_in_char_b7 = PB_in_char.7



//--------------------------------------------------------------------------------------------
//  void send_PA_out_char(char c)                   send char to PA port buffer
//
//  Public routine send_PA_out_char(c) allows main loop to send char to PA port buffer.
//  If buffer is full, we update diag_code, and discard char.

void send_PA_out_char(char c)
{
  if(PA_out_buf_count == PA_OUTBUF_SIZE)                 // if buffer full,
  {
    diag_code = DIAG_PA_OUTBUF_FULL;                     //   update diag_code, and discard char
  }
  else
  {
    *PA_out_buf_wr++ = c;                                // put char into buffer, and inc ptr
    if(PA_out_buf_wr == (PA_out_buf + PA_OUTBUF_SIZE))   // did write ptr wrap?
      PA_out_buf_wr = PA_out_buf;                        // if so, reset it to start of buffer
    PA_out_buf_count++;                                  // increment char count
  }
}


//--------------------------------------------------------------------------------------------
//  char get_PA_in_char(void)                       get next char (if any) from PA port buffer
//
//  Public routine c = get_PA_in_char() allows main loop to get next char (if any)
//  from PA port buffer.  Returns 0 if the buffer is empty.

char get_PA_in_char(void)
{
  char c=0;

  // ----- get char from buffer, if there is one available:
  if(PA_in_buf_count > 0)                              // check whether there is any data
  {
    c = *PA_in_buf_rd++;                               // read char from buffer
    if(PA_in_buf_rd == (PA_in_buf + PA_INBUF_SIZE))    // did read ptr wrap?
      PA_in_buf_rd = PA_in_buf;                        // if so, reset it to start of buffer
    PA_in_buf_count--;                                 // decrement char count
  }

  return(c);
}


//--------------------------------------------------------------------------------------------
//  void send_PB_out_char(char c)                   send char to PB port buffer
//
//  Public routine send_PB_out_char(c) allows main loop to send char to PB port buffer.
//  If buffer is full, we update diag_code, and discard char.

void send_PB_out_char(char c)
{

  if(PB_out_buf_count == PB_OUTBUF_SIZE)                 // if buffer full,
  {
    diag_code = DIAG_PB_OUTBUF_FULL;                     //   update diag_code, and discard char
  }
  else
  {
    *PB_out_buf_wr++ = c;                                // put char into buffer, and inc ptr
    if(PB_out_buf_wr == (PB_out_buf + PB_OUTBUF_SIZE))   // did write ptr wrap?
      PB_out_buf_wr = PB_out_buf;                        // if so, reset it to start of buffer
    PB_out_buf_count++;                                  // increment char count
  }
}


//--------------------------------------------------------------------------------------------
//  char get_PB_in_char(void)                       get next char (if any) from PB port buffer
//
//  Public routine c = get_PB_in_char() allows main loop to get next char (if any)
//  from PB port buffer.  Returns 0 if the buffer is empty.

char get_PB_in_char(void)
{
  char c=0;

  // ----- get char from buffer, if there is one available:
  if(PB_in_buf_count > 0)                              // check whether there is any data
  {
    c = *PB_in_buf_rd++;                               // read char from buffer
    if(PB_in_buf_rd == (PB_in_buf + PB_INBUF_SIZE))    // did read ptr wrap?
      PB_in_buf_rd = PB_in_buf;                        // if so, reset it to start of buffer
    PB_in_buf_count--;                                 // decrement char count
  }

  return(c);
}


//--------------------------------------------------------------------------------------------
//  void output_PA_bit(int1 PA_bit)                 copy PA_bit value to TTY-Port-A pin
//
//  Private routine output_PA_bit(PA_bit) allows T1_isr to copy PA_bit value to TTY-Port-A pin
//  designated by global PA_out_bit.  Global TTY_port_image is updated as well.
//  When tty-port-A is configured as the TU port, tu_data_inversion is allowed.

void output_PA_bit(int1 PA_bit)
{
  if( tu_data_inversion && (PA_out_bit == TU_OUT_BIT) )
    PA_bit ^= 1;                       // invert bit

  if(PA_bit)
    TTY_port_image |= PA_out_bit;      // set bit
  else
    TTY_port_image &= ~PA_out_bit;     // clear bit
  output_b(TTY_port_image);
}


//--------------------------------------------------------------------------------------------
//  void output_PB_bit(int1 PB_bit)                 copy PB_bit value to TTY-Port-B pin
//
//  Private routine output_PB_bit(PB_bit) allows T1_isr to copy PB_bit value to TTY-Port-B pin
//  designated by global PB_out_bit.  Global TTY_port_image is updated as well.
//  When tty-port-B is configured as the TU port, tu_data_inversion is allowed.

void output_PB_bit(int1 PB_bit)
{
  if( tu_data_inversion && (PB_out_bit == TU_OUT_BIT) )
    PB_bit ^= 1;                       // invert bit

  if(PB_bit)
    TTY_port_image |= PB_out_bit;      // set bit
  else
    TTY_port_image &= ~PB_out_bit;     // clear bit
  output_b(TTY_port_image);
}


//--------------------------------------------------------------------------------------------
//  void T1_isr(void)                               timer-1 intr is called every 125 us
//
//  Timer-1 is a 125 us interrupt handler that provides software uarts for the TTY-Port-A
//  and TTY-Port-B ports.  Each port is toggled in succession, so the port sample rate is 250us.
//  Each port has 250-byte buffers on input streams (and 250-byte on output), with error codes loaded
//  into global diag_code if the buffers overflow (chars are lost on overflow).
//  Main can send error message, if desired.
//  Assignment of a specific physical port to TTY-Port-A or TTY-Port-B is done in main -- eg:
//            PA_in_bit =  TU_IN_BIT;                        // input pin for TU
//            PA_out_bit = TU_OUT_BIT;                       // output pin for TU
//            PA_bitrate = TU_bitrate - 1;                   // set to 1 less, for uart logic
//            PA_numbits = TU_numbits;                       // 5, 6, 7, or 8 bits
//  Option: when either tty-port-A or tty-port-B is configured as the TU port, tu_data_inversion is allowed.
//  Option: when tty-port-A input is idle, send LTRS char if tx_diddle_insertion is enabled.
//
//
//    For 20MHz clock, with T1 reloaded for rollover count of 625 and divider set to 1,
//    this int routine is called every 125 us (8000 times/sec):
//
//            Clock MHz                   20000000
//     -------------------------   =   -------------- = 8000 times/sec
//     (4)(T1-count)(T1-divider)        (4)(625)(1)
//
//    T1_isr_index cycles 1 to 4, to select current tty port ==> ie: tty ports are sampled at 500 us
//
//    Want T1RELOAD to result in 625 counts before T1 rollover (65535->0);
//    adjusted for overhead:

#DEFINE         T1RELOAD            65536 - 625 + 50 // 50 corrects for overhead
//#DEFINE         T1RELOAD            65536 - 625    // (see jitter note below)

#INT_TIMER1
void T1_isr(void)                                    // this occurs every 125-us
{
  int8 tmp;

  // ========== start of timer-1 isr ==========

  #IFDEF T1_INT_MON
    output_high (T1_INT_MON);                         // TIMER-1 isr monitor pin
  #ENDIF

  set_timer1 (T1RELOAD);                              // reload timer-1 every intr, with fixed
                                                      //  amount of correction

  //    Note:  I saw jitter when doing it this way:
  //set_timer1 (T1RELOAD + get_timer1());             // reload timer-1 every intr, correcting
                                                      //  for overhead by reading timer


  // ========== T1_isr_index toggles 0 to 1, to select current tty port:
  switch (T1_isr_index)
  {

    case 0:
      // =======================================================================
      // ==============================  TTY-Port-A  ===========================
      // =======================================================================

      // ========== PA port char input -- do input first for less sample jitter ==========


      // ----- read port, mask for desired input bit, and assign to T1_bit:
      tmp = input_b();

      if( tu_data_inversion && (PA_in_bit == TU_IN_BIT) )
        tmp = ~tmp;     // invert

      if (tmp & PA_in_bit)
        T1_bit = 1;
      else
        T1_bit = 0;

      // ----- wait for start (mark to space transition):
      if (PA_in_state == TTY_IDLE)
      {
        if (!T1_bit)
        {
          PA_in_state = TTY_START;                        // sync to start edge
          PA_in_bit_delay = PA_bitrate / 2;               // set 1/2-bit delay for sampling
        }
      }

      // ----- if we are at bit sample point, check it:
      if (PA_in_bit_delay == 0)
      {
        PA_in_bit_delay = PA_bitrate;                     // set 1-bit delay for sampling next bit

        switch (PA_in_state)                              // which bit are we sampling now?
        {
          case TTY_START:                                 // verify start (should be mid-bit)
            if (!T1_bit)
            {
              PA_in_state = TTY_BIT_0;
              PA_in_char = 0;                             // init char to 0
            }
            else
              PA_in_state = TTY_IDLE;                     // oops - not actually a start bit
            break;

          case TTY_BIT_0:
            PA_in_char_b0 = T1_bit;
            PA_in_state = TTY_BIT_1;
            break;

          case TTY_BIT_1:
            PA_in_char_b1 = T1_bit;
            PA_in_state = TTY_BIT_2;
            break;

          case TTY_BIT_2:
            PA_in_char_b2 = T1_bit;
            PA_in_state = TTY_BIT_3;
            break;

          case TTY_BIT_3:
            PA_in_char_b3 = T1_bit;
            PA_in_state = TTY_BIT_4;
            break;

          case TTY_BIT_4:
            PA_in_char_b4 = T1_bit;
            if (PA_numbits > 5)
              PA_in_state = TTY_BIT_5;                    // get more bits
            else
              PA_in_state = TTY_STOP;                     // else, now have 5 bits
            break;

          case TTY_BIT_5:
            PA_in_char_b5 = T1_bit;
            if (PA_numbits > 6)
              PA_in_state = TTY_BIT_6;                    // get more bits
            else
              PA_in_state = TTY_STOP;                     // else, now have 6 bits
            break;

          case TTY_BIT_6:
            PA_in_char_b6 = T1_bit;
            if (PA_numbits > 7)
              PA_in_state = TTY_BIT_7;                    // get more bits
            else
              PA_in_state = TTY_STOP;                     // else, now have 7 bits
            break;

          case TTY_BIT_7:
            PA_in_char_b7 = T1_bit;
            PA_in_state = TTY_STOP;
            break;

          case TTY_STOP:                                  // verify first stop bit
            //  (we will ignore second stop bit -- baudot uses 1.42 stop bits, ascii uses 2 stop bits)
            //  (if we get bad first stop bit, we just discard char

            PA_in_state = TTY_IDLE;                       // ready for next char

            if (T1_bit)                                   // check stop bit
            {
              if(PA_in_buf_count == PA_INBUF_SIZE)                // if buffer full,
              {
                diag_code = DIAG_PA_INBUF_FULL;                   //   update diag_code, and discard char
              }
              else
              {
                *PA_in_buf_wr++ = PA_in_char;                     // put char into buffer, and inc ptr
                if(PA_in_buf_wr == (PA_in_buf + PA_INBUF_SIZE))   // did write ptr wrap?
                  PA_in_buf_wr = PA_in_buf;                       // if so, reset it to start of buffer
                PA_in_buf_count++;                                // increment char count
              }
            }
            break;

          default:
            PA_in_state = TTY_IDLE;                       // (will get here when idle)
        }
      }

      // ----- if not at bit sample point, decrement delay counter:
      else
        --PA_in_bit_delay;


      // ========== PA port char output ==========

      // ----- if we are at next bit transition point, send it:
      if (PA_out_bit_delay == 0)
      {
        PA_out_bit_delay = PA_bitrate;                    // init 1-bit delay for sending next bit

        switch (PA_out_state)                             // which bit are we sending now?
        {
          case TTY_START:
            output_PA_bit(0);                             // send start bit (space)
            PA_out_state = TTY_BIT_0;
            break;

          case TTY_BIT_0:
            output_PA_bit(PA_out_char_b0);
            PA_out_state = TTY_BIT_1;
            break;

          case TTY_BIT_1:
            output_PA_bit(PA_out_char_b1);
            PA_out_state = TTY_BIT_2;
            break;

          case TTY_BIT_2:
            output_PA_bit(PA_out_char_b2);
            PA_out_state = TTY_BIT_3;
            break;

          case TTY_BIT_3:
            output_PA_bit(PA_out_char_b3);
            PA_out_state = TTY_BIT_4;
            break;

          case TTY_BIT_4:
            output_PA_bit(PA_out_char_b4);
            if (PA_numbits > 5)
              PA_out_state = TTY_BIT_5;                   // send more bits
            else
              PA_out_state = TTY_STOP;                    // else, have sent 5 bits (now send 1.5 stop)
            break;

          case TTY_BIT_5:
            output_PA_bit(PA_out_char_b5);
            if (PA_numbits > 6)
              PA_out_state = TTY_BIT_6;                   // send more bits
            else
              PA_out_state = TTY_STOP;                    // else, have sent 6 bits (now send 1.5 stop)
            break;

          case TTY_BIT_6:
            output_PA_bit(PA_out_char_b6);
            if (PA_numbits > 7)
              PA_out_state = TTY_BIT_7;                   // send more bits
            else
              PA_out_state = TTY_2STOP;                   // have sent 7 bits (now send 2 stop)
            break;

          case TTY_BIT_7:
            output_PA_bit(PA_out_char_b7);
            PA_out_state = TTY_2STOP;                     // have sent 8 bits  (now send 2 stop)
            break;

          case TTY_STOP:                                  // 1.5 stop bits for baudot
            output_PA_bit(1);                             // send stop bit (mark)
            PA_out_bit_delay = PA_bitrate + PA_bitrate/2;
            PA_out_state = TTY_TXEND;
            break;

          case TTY_2STOP:                                 // 2 stop bits for ascii
            output_PA_bit(1);                             // send stop bit (mark)
            PA_out_bit_delay = 2 * PA_bitrate;
            PA_out_state = TTY_TXEND;
            break;

          case TTY_TXEND:
            PA_out_state = TTY_IDLE;                      // char has been sent
            break;

          default:
            PA_out_state = TTY_IDLE;                      // (will get here when idle)
        }
      }

      // ----- if not at bit transition point, decrement delay counter:
      else
        --PA_out_bit_delay;

      // -----  get char from PA port output buffer, if avail, and send it next:
      if (PA_out_state == TTY_IDLE)                           // don't do anything unless idle
      {
        if(PA_out_buf_count > 0)                              // check whether there is any data
        {
          PA_out_char = *PA_out_buf_rd++;                     // read char from buffer
          if(PA_out_buf_rd == (PA_out_buf + PA_OUTBUF_SIZE))  // did read ptr wrap?
            PA_out_buf_rd = PA_out_buf;                       // if so, reset it to start of buffer
          PA_out_buf_count--;                                 // decrement char count
          PA_out_bit_delay = 0;                               // zero this to start
          PA_out_state = TTY_START;                           // ready to send char
        }
        // -----  when idle, send LTRS char if tx_diddle_insertion is enabled:
        else
        {
          if(tx_diddle_insertion)
            PA_out_char = LTRS;
        }
      }

      // ----- set index to sample TTY-Port-B on next intr:
      T1_isr_index = 1;

      break;



    case 1:
      // =======================================================================
      // ==============================  TTY-Port-B  ===========================
      // =======================================================================

      // ========== PB port char input -- do input first for less sample jitter ==========


      // ----- read port, mask for desired input bit, and assign to T1_bit:
      tmp = input_b();

      if( tu_data_inversion && (PB_in_bit == TU_IN_BIT) )
        tmp = ~tmp;

      if (tmp & PB_in_bit)
        T1_bit = 1;
      else
        T1_bit = 0;


      // ----- wait for start (mark to space transition):
      if (PB_in_state == TTY_IDLE)
      {
        if (!T1_bit)
        {
          PB_in_state = TTY_START;                        // sync to start edge
          PB_in_bit_delay = PB_bitrate / 2;               // set 1/2-bit delay for sampling
        }
      }

      // ----- if we are at bit sample point, check it:
      if (PB_in_bit_delay == 0)
      {
        PB_in_bit_delay = PB_bitrate;                     // set 1-bit delay for sampling next bit

        switch (PB_in_state)                              // which bit are we sampling now?
        {
          case TTY_START:                                 // verify start (should be mid-bit)
            if (!T1_bit)
            {
              PB_in_state = TTY_BIT_0;
              PB_in_char = 0;                             // init char to 0
            }
            else
              PB_in_state = TTY_IDLE;                     // oops - not actually a start bit
            break;

          case TTY_BIT_0:
            PB_in_char_b0 = T1_bit;
            PB_in_state = TTY_BIT_1;
            break;

          case TTY_BIT_1:
            PB_in_char_b1 = T1_bit;
            PB_in_state = TTY_BIT_2;
            break;

          case TTY_BIT_2:
            PB_in_char_b2 = T1_bit;
            PB_in_state = TTY_BIT_3;
            break;

          case TTY_BIT_3:
            PB_in_char_b3 = T1_bit;
            PB_in_state = TTY_BIT_4;
            break;

          case TTY_BIT_4:
            PB_in_char_b4 = T1_bit;
            if (PB_numbits > 5)
              PB_in_state = TTY_BIT_5;                    // get more bits
            else
              PB_in_state = TTY_STOP;                     // else, now have 5 bits
            break;

          case TTY_BIT_5:
            PB_in_char_b5 = T1_bit;
            if (PB_numbits > 6)
              PB_in_state = TTY_BIT_6;                    // get more bits
            else
              PB_in_state = TTY_STOP;                     // else, now have 6 bits
            break;

          case TTY_BIT_6:
            PB_in_char_b6 = T1_bit;
            if (PB_numbits > 7)
              PB_in_state = TTY_BIT_7;                    // get more bits
            else
              PB_in_state = TTY_STOP;                     // else, now have 7 bits
            break;

          case TTY_BIT_7:
            PB_in_char_b7 = T1_bit;
            PB_in_state = TTY_STOP;
            break;

          case TTY_STOP:                                  // verify first stop bit
            //  (we will ignore second stop bit -- baudot uses 1.42 stop bits, ascii uses 2 stop bits)
            //  (if we get bad first stop bit, we just discard char

            PB_in_state = TTY_IDLE;                       // ready for next char

            if (T1_bit)                                   // check stop bit
            {
              if(PB_in_buf_count == PB_INBUF_SIZE)                // if buffer full,
              {
                diag_code = DIAG_PB_INBUF_FULL;                   //   update diag_code, and discard char
              }
              else
              {
                *PB_in_buf_wr++ = PB_in_char;                     // put char into buffer, and inc ptr
                if(PB_in_buf_wr == (PB_in_buf + PB_INBUF_SIZE))   // did write ptr wrap?
                  PB_in_buf_wr = PB_in_buf;                       // if so, reset it to start of buffer
                PB_in_buf_count++;                                // increment char count
              }
            }
            break;

          default:
            PB_in_state = TTY_IDLE;                       // (will get here when idle)
        }
      }

      // ----- if not at bit sample point, decrement delay counter:
      else
        --PB_in_bit_delay;


      // ========== PB port char output ==========

      // ----- if we are at next bit transition point, send it:
      if (PB_out_bit_delay == 0)
      {
        PB_out_bit_delay = PB_bitrate;                    // init 1-bit delay for sending next bit

        switch (PB_out_state)                             // which bit are we sending now?
        {
          case TTY_START:
            output_PB_bit(0);                             // send start bit (space)
            PB_out_state = TTY_BIT_0;
            break;

          case TTY_BIT_0:
            output_PB_bit(PB_out_char_b0);
            PB_out_state = TTY_BIT_1;
            break;

          case TTY_BIT_1:
            output_PB_bit(PB_out_char_b1);
            PB_out_state = TTY_BIT_2;
            break;

          case TTY_BIT_2:
            output_PB_bit(PB_out_char_b2);
            PB_out_state = TTY_BIT_3;
            break;

          case TTY_BIT_3:
            output_PB_bit(PB_out_char_b3);
            PB_out_state = TTY_BIT_4;
            break;

          case TTY_BIT_4:
            output_PB_bit(PB_out_char_b4);
            if (PB_numbits > 5)
              PB_out_state = TTY_BIT_5;                   // send more bits
            else
              PB_out_state = TTY_STOP;                    // else, have sent 5 bits (now send 1.5 stop)
            break;

          case TTY_BIT_5:
            output_PB_bit(PB_out_char_b5);
            if (PB_numbits > 6)
              PB_out_state = TTY_BIT_6;                   // send more bits
            else
              PB_out_state = TTY_STOP;                    // else, have sent 6 bits (now send 1.5 stop)
            break;

          case TTY_BIT_6:
            output_PB_bit(PB_out_char_b6);
            if (PB_numbits > 7)
              PB_out_state = TTY_BIT_7;                   // send more bits
            else
              PB_out_state = TTY_2STOP;                   // have sent 7 bits (now send 2 stop)
            break;

          case TTY_BIT_7:
            output_PB_bit(PB_out_char_b7);
            PB_out_state = TTY_2STOP;                     // have sent 8 bits  (now send 2 stop)
            break;

          case TTY_STOP:                                  // 1.5 stop bits for baudot
            output_PB_bit(1);                             // send stop bit (mark)
            PB_out_bit_delay = PB_bitrate + PB_bitrate/2;
            PB_out_state = TTY_TXEND;
            break;

          case TTY_2STOP:                                 // 2 stop bits for ascii
            output_PB_bit(1);                             // send stop bit (mark)
            PB_out_bit_delay = 2 * PB_bitrate;
            PB_out_state = TTY_TXEND;
            break;

          case TTY_TXEND:
            PB_out_state = TTY_IDLE;                      // char has been sent
            break;

          default:
            PB_out_state = TTY_IDLE;                      // (will get here when idle)
        }
      }

      // ----- if not at bit transition point, decrement delay counter:
      else
        --PB_out_bit_delay;

      // -----  get char from PB port output buffer, if avail, and send it next:
      if (PB_out_state == TTY_IDLE)                           // don't do anything unless idle
      {
        if(PB_out_buf_count > 0)                              // check whether there is any data
        {
          PB_out_char = *PB_out_buf_rd++;                     // read char from buffer
          if(PB_out_buf_rd == (PB_out_buf + PB_OUTBUF_SIZE))  // did read ptr wrap?
            PB_out_buf_rd = PB_out_buf;                       // if so, reset it to start of buffer
          PB_out_buf_count--;                                 // decrement char count
          PB_out_bit_delay = 0;                               // zero this to start
          PB_out_state = TTY_START;                           // ready to send char
        }
      }

      // ----- set index to sample TTY-Port-A on next intr::
      T1_isr_index = 0;

      break;

  }

  // ========== end of timer-1 isr ==========

  #IFDEF T1_INT_MON
    output_low (T1_INT_MON);                                // TIMER-1 isr monitor pin
  #ENDIF

}



