//============================================================================================
//      tc_subs.c             ver 2.2   (x/x/08)
//
//      copyright (c) 2001-2008 Vaux Electronics, Inc.
//============================================================================================


//============================================================================================
//
//  Public routine connect(mod) initiates connections between pc/tu/tty ports.
//  The following connection-config globals must be loaded prior to call -- this routine
//  saves these in eeprom, and also uses them to set port-control globals:
//      port_A_port, port_A_bits, port_A_rate, port_B_port, port_B_bits, port_B_rate
//  Also, mod is passed -- this is saved to global mode, and saved in eeprom.
//  The following port-control globals are loaded here:
//      PA_in_bit, PA_out_bit, PA_numbits, PA_bitrate,
//      PB_in_bit, PB_out_bit, PB_numbits, PB_bitrate
//  These port-control globals control the timer-interrupt routine performing serial i/o
//  to the ports.  These globals also direct the connection/option logic in the main loop.
//
//  Public routine restore_factory_settings() restores programmable params to factory settings.
//
//  Public routine send_FP_tx_byte(c) allows main loop to send byte to serial Front-Panel display
//  using software-serial output to port pin (restoring normal sci serial on exit)
//
//  Public routine read_conn_params_from_eeprom() reads connect params from eeprom, and loads globals
//
//  Public routine write_conn_params_to_eeprom() writes connect params to eeprom, from globals
//
//  Public routine read_prog_params_from_eeprom() reads prog params from eeprom, and loads globals
//
//  Public routine write_prog_params_to_eeprom() writes prog params to eeprom, from globals
//
//  Public routine br=port_bitrate(rate) returns bitrate based on 60- to 100-wpm, or 110-baud
//
//  Public routine pi=port_input_bit(port) returns input bit based on port number
//
//  Public routine po=port_output_bit(port) returns output bit based on port number
//
//  Public routine p=limit(param, min, max) limits param to min/max range
//
//  Public routine w=wpm_limit(wpm) limits wpm to 60, 66, 75, or 100
//
//  Public routine change_output_signal(out, sta) changes state of pin out, to new state sta.
//  Message is sent to update status (or error if bad out param).
//  Global A_port_image is updated before output.
//
//  Public routine s=query_output_signal(out) returns state of pin out from global A_port_image.
//
//  Public routine b=control_pb_pressed() checks CONTROL pushbutton, and if it has been
//  pressed and released, returns TRUE (else FALSE).
//
//  Public routine c=ustty_to_ascii(c) converts USTTY-Baudot to ASCII-67.
//  Keeps track of incoming LTRS/FIGS case in global baudot_in_current_case (presumes LTRS initially).
//  Returns NUL for BLANK.
//  Optionally returns SO for FIGS, and SI for LTRS (after updating global baudot_in_current_case).
//
//  Public routine c=ascii_to_ustty(c) converts ASCII-67 to USTTY-Baudot.
//  Except for a few control chars, only the chars from SP to Z are mapped.
//  Returns BLANK for NUL, and for unmapped chars.
//  Keeps track of outgoing LTRS/FIGS case in global baudot_out_current_case.
//  Returns FIGS for SO (^N), and LTRS for SI (^O), for testing or special use (note that
//  needed FIGS/LTRS chars are sent automatically -- SO/SI are only used for inserting extras).
//  Calling routine to check baudot_out_new_case and send it if non-zero (FIGS or LTRS),
//  then send returned USTTY char.
//
//  Public routine rx_options_and_send_PB_out_char(c, format) gets baudot/ascii output char, and filters it
//  through rx stream options before output to tty-port B (tty-rx).
//  For BAUDOT format, detects stings for Start-of-Message, End-of-Message, Conditioning-Code, SelCal-Station,
//  SelCal-All, and WRU.  Provides autostart motor powering, auto-crlf insertion, unshifts, and rx-diddle filter.
//  Also keeps track of outgoing LTRS/FIGS case.
//  For ASCII format, provides motor powering for autostart-char (only), and auto-crlf insertion.
//  Uses a shitload of globals.
//
//============================================================================================
//
//      to do:
//          -
//      tc_subs.c             ver 2.2   (x/x/08)
//          - added send_FP_tx_byte(int8 c) and misc changes
//
//      tc_subs.c             ver 2.0   (6/20/06)
//          - added restart_wdt() to control_pb_pressed()
//          - loading lcd startup screen in restore_factory_settings()
//          - replaced byte (was typedef) with int8
//
//      tc_subs.c             ver 1.0   (9/8/04)
//          - created from ascii/baudot conversion stuff in tty.c (from tty232 project)
//
//============================================================================================


//  ----- prototypes (put in tc_main.c so available to all):
//void connect(int8 mod);                                                         // initiates connections between pc/tu/tty ports
//void restore_factory_settings(void);                                            // programmable params to factory settings
//void send_FP_tx_byte(int8 c);                                                   // send byte to Front-Panel display
//void read_conn_params_from_eeprom(void);                                        // reads connect params from eeprom, and loads globals
//void write_conn_params_to_eeprom(void);                                         // writes connect params to eeprom, from globals
//void read_prog_params_from_eeprom(void);                                        // reads prog params from eeprom, and loads globals
//void write_prog_params_to_eeprom(void);                                         // writes prog params to eeprom, from globals
//int8 port_bitrate(int8 rate);                                                   // returns bitrate based on 60- to 100-wpm, or 110-baud
//int8 port_input_bit(int8 port);                                                 // returns input bit based on port number
//int8 port_output_bit(int8 port);                                                // returns output bit based on port number
//int8 limit(int8 param, int8 min, int8 max);                                     // limits param to min/max range
//int8 wpm_limit(int8 wpm);                                                       // limits wpm to 60, 66, 75, or 100
//void change_output_signal(int8 out, int8 sta);                                  // changes state of output pin
//int8 query_output_signal(int8 out);                                             // returns state of output pin
//int8 control_pb_pressed(void);                                                  // if CONTROL PB pressed/released, return TRUE
//char ustty_to_ascii(char c);                                                    // converts USTTY-Baudot to ASCII-67
//char ascii_to_ustty(char c);                                                    // converts ASCII-67 to USTTY-Baudot
//void rx_options_and_send_PB_out_char(char c, int8 format);                      // baudot/ascii output char piped through rx stream options


// ----- defines:
#ifndef       DEBUG_CONNECTION
  #define     DEBUG_CONNECTION            OFF                                   // main sets ON to turn on connection debug
#endif


// ----- globals:



//--------------------------------------------------------------------------------------------
//  void connect(int8 mod)                          initiates connections between pc/tu/tty ports
//
//  Public routine connect(mod) initiates connections between pc/tu/tty ports.
//  The following connection-config globals must be loaded prior to call -- this routine
//  saves these in eeprom, and also uses them to set port-control globals:
//      port_A_port, port_A_bits, port_A_rate, port_B_port, port_B_bits, port_B_rate
//  Also, mod is passed -- this is saved to global mode, and saved in eeprom.
//  The following port-control globals are loaded here:
//      PA_in_bit, PA_out_bit, PA_numbits, PA_bitrate,
//      PB_in_bit, PB_out_bit, PB_numbits, PB_bitrate
//  These port-control globals control the timer-interrupt routine performing serial i/o
//  to the ports.  These globals also direct the connection/option logic in the main loop.
//
//  TC Connect Commands:
//      connect as mode-1:    /.TW,1,2,ptu,spd <cr>
//      connect as mode-2:    /.TW,2,1,ptu <cr>
//      connect as mode-3:    /.TW,3,3,ptu,spx,bit <cr>
//      connect as mode-4:    /.TW,4,2,pty,spd <cr>
//      connect as mode-5:    /.TW,5,2,spd,pty <cr>
//      connect as mode-6:    /.TW,6,4,pta,spa,ptb,spb <cr>
//      connect as mode-10:   /.TW,10,0 <cr>
//      connect as mode-11:   /.TW,11,0 <cr>

void connect(int8 mod)
{
  // ----- init port image for loop/TU ports all marking or spacing:
  if(unconn_loops_mark)
    TTY_port_image = ALL_LOOPS_MARK_TU_MARK;                                    // three tty loops and TU port marking
  else
    TTY_port_image = ALL_LOOPS_SPACE_TU_MARK;                                   // three tty loops to space; TU marking

  // ----- update mode:
  mode = mod;

  // ----- connection mode determines port param setup:
  switch (mode)
  {

  // ----- mode 1:  Ascii PC to Baudot TTY/TU                                   // cmd:   /.TW,1,2,ptu,spd <cr>
  case 1:
  // ----- mode 2:  Ascii PC to 110-Ascii TTY/TU                                // cmd:   /.TW,2,1,ptu <cr>
  case 2:
    PB_bitrate = port_bitrate(port_B_rate);                                     // map 60- to 100-wpm, or 110-baud, to bitrate
    break;

  // ----- mode 3:  Raw PC to Raw TTY/TU                                        // cmd:   /.TW,3,3,ptu,spx,bit <cr>
  case 3:
    PB_bitrate = port_B_rate - 1;                                               // set to 1 less, for uart logic
    break;

  // ----- mode 4:  Baudot TU to Baudot TTY                                     // cmd:   /.TW,4,2,pty,spd <cr>
  case 4:
  // ----- mode 5:  Baudot TU to 110-Ascii TTY                                  // cmd:   /.TW,5,2,spd,pty <cr>
  case 5:
  // ----- mode 6:  Baudot ports w/ speed change                                // cmd:   /.TW,6,4,pta,spa,ptb,spb <cr>
  case 6:
    PA_bitrate = port_bitrate(port_A_rate);                                     // map 60- to 100-wpm to bitrate
    PB_bitrate = port_bitrate(port_B_rate);                                     // map 60- to 100-wpm, or 110-baud, to bitrate
    break;

  // ----- connect mode-10:  No-connection; tty loops to space  // cmd:   /.TW,10,0 <cr>
  case 10:
    TTY_port_image = ALL_LOOPS_SPACE_TU_MARK;                                   // three tty loops to space; TU marking
    break;

  // ----- connect mode-11:  No-connection; tty loops to mark   // cmd:   /.TW,11,0 <cr>
  case 11:
    TTY_port_image = ALL_LOOPS_MARK_TU_MARK;                                    // three tty loops and TU port marking
    break;

  default:
    // ----- invalid mode param value:
    diag_code = DIAG_CONNECT_MODE_PARAM;
    return;
  }

  // ----- init loop/TU ports and connection vars:
  baudot_in_current_case = 0;                                                   // table offset: 0 for LTRS, 32 for FIGS (presumes LTRS initially)
  baudot_out_current_case = LTRS_FIGS_UNKNOWN;                                  // actual case:  0x1F for LTRS, 0x1B for FIGS (initially unknown)
  baudot_out_new_case = 0;                                                      // 0 for no tx case change, else actual case
  tx_cr_char_count = 0;                                                         // tx-options param
  rx_cr_char_count = 0;                                                         // rx-options param
  start_of_mssg_matches = 0;                                                    // rx-options param
  end_of_mssg_matches = 0;                                                      // rx-options param
  cond_code_matches = 0;                                                        // rx-options param
  selcal_station_matches = 0;                                                   // rx-options param
  selcal_all_matches = 0;                                                       // rx-options param
  flags_1 = 0;                                                                  // rx-options param

  // ----- config remaining port params:
  PA_in_bit  = port_input_bit(port_A_port);                                     // map port num to input pin
  PA_out_bit = port_output_bit(port_A_port);                                    // map port num to output pin
  PA_numbits = port_A_bits;

  PB_in_bit  = port_input_bit(port_B_port);                                     // map port num to input pin
  PB_out_bit = port_output_bit(port_B_port);                                    // map port num to output pin
  PB_numbits = port_B_bits;

  // ----- init loop/TU ports, and turn off motors and ptt:
  TTY_port_image |= PA_out_bit;                                                 // if PA used, set out bit to mark
  TTY_port_image |= PB_out_bit;                                                 // if PB used, set out bit to mark

  if(tu_data_inversion)
    TTY_port_image &= ~TU_OUT_BIT;                                              // clear TU bit for inverted mark

  PORT_B = TTY_port_image;                                                      // output TTY_port_image, as initialized above

  MOT_port_image = ALL_MOTORS_OFF;                                              // output, set motors (and PTT) off
  PORT_A = MOT_port_image;

  // ----- save connect-config params:
  write_conn_params_to_eeprom();


  #if (DEBUG_CONNECTION)
    printf(send_sci_tx_byte, "DEBUG_CONNECTION (connect): mode: %u\r\n", mode);
    printf(send_sci_tx_byte, "  port_A_port: %u, port_A_rate: %u, port_A_bits: %u\r\n", port_A_port, port_A_rate, port_A_bits);
    printf(send_sci_tx_byte, "  port_B_port: %u, port_B_rate: %u, port_B_bits: %u\r\n", port_B_port, port_B_rate, port_B_bits);
    printf(send_sci_tx_byte, "  PA_in_bit: %u, PA_out_bit: %u, PA_bitrate: %u, PA_numbits: %u\r\n", PA_in_bit, PA_out_bit, PA_bitrate, PA_numbits);
    printf(send_sci_tx_byte, "  PB_in_bit: %u, PB_out_bit: %u, PB_bitrate: %u, PB_numbits: %u\r\n", PB_in_bit, PB_out_bit, PB_bitrate, PB_numbits);
  #endif
}


//--------------------------------------------------------------------------------------------
//  void restore_factory_settings(void)             programmable params to factory settings
//
//  Public routine restore_factory_settings() restores programmable params to factory settings.


// ----- factory settings for programmable baudot string arrays:
int8 const auto_crlf_str_fs[8] = { 8,8,2,31,31,0,0,0 };                         // Auto-CRLF string: CR CR LF LTRS LTRS (in baudot)

#if (STREAM_OPTIONS)
int8 const start_of_mssg_str_fs[8] = { 4,4,4,4,4,8,8,2 };                       // Start-of-Message string:  SP SP SP SP SP CR CR LF (in baudot)
int8 const end_of_mssg_str_fs[8] = { 2,12,12,12,12,0,0,0 };                     // End-of-Message string:  LF N N N N (in baudot)
int8 const cond_code_str_fs[8] = { 4,4,4,4,4,8,8,2 };                           // Conditioning-Code string:  SP SP SP SP SP CR CR LF (in baudot)
int8 const selcal_station_str_fs[8] = { 16,16,21,14,24,12,12,0 };               // SelCal-Station string:  T T Y C O N N (in baudot)
int8 const selcal_all_str_fs[8] = { 17,14,17,14,0,0,0,0 };                      // SelCal-All string:  Z C Z C (in baudot)
#endif


void restore_factory_settings(void)
{
  int8 i;

  // ----- init programmable params, and write to eeprom:
  for (i=0; i<=7; ++i)
  {
    auto_crlf_str[i] = auto_crlf_str_fs[i];
#if (STREAM_OPTIONS)
    start_of_mssg_str[i] = start_of_mssg_str_fs[i];
    end_of_mssg_str[i] = end_of_mssg_str_fs[i];
    cond_code_str[i] = cond_code_str_fs[i];
    selcal_station_str[i] = selcal_station_str_fs[i];
    selcal_all_str[i] = selcal_all_str_fs[i];
#endif
  }

  max_chars_per_line = 72;
  motor_power_mode = MOT_PWR_OFF;
  prog_flags_1 = 0;
  prog_flags_2 = 0;

  write_prog_params_to_eeprom();                                                // writes prog params to eeprom, from globals


  // ----- initialize connection mode 10 -- all tty loops to space:
  port_A_bits = 0;                                                             // (not used)
  port_A_port = 0;                                                             // (not used)
  port_A_rate = 0;                                                             // (not used)
  port_B_bits = 0;                                                             // (not used)
  port_B_port = 0;                                                             // (not used)
  port_B_rate = 0;                                                             // (not used)
  connect(10);                                                                 // initiates connection and updates eeprom


  // ----- init lcd startup screen:
#if (LCD_STUFF)
  lcd_load_startup_screen();
#endif


  // ----- this indicates that memory has been initialized:
  write_eeprom(INT_EE_FIRMWARE_VER_H, FIRMWARE_VERSION_HI);
  write_eeprom(INT_EE_FIRMWARE_VER_L, FIRMWARE_VERSION_LO);


  // --- send  -.TC,250,0 <cr>  status message:
  //        put delays around it  so there is a noticable extra flash at boot time
  //        (if CONTROL pb was held at power-up to restore factory settings)
  delay_ms (100);
  vp_msg[0] = 250;                                                              // mssg-id
  vp_msg[1] = 0;                                                                // num params
  send_tc_mssg_params(2);                                                       // send mssg and flash status led
  delay_ms (100);
}


//--------------------------------------------------------------------------------------------
//  void send_FP_tx_byte(int8 c)                    send byte to Front-Panel display
//
//  Public routine send_FP_tx_byte(c) allows main loop to send byte to serial Front-Panel display
//  using software-serial output to port pin (restoring normal sci serial on exit)
//
//  ----- ascii control codes for VFD display:
//  NUL       0x00              // ctrl-(space)  or ctrl-@
//  SOH       0x01              // ctrl-A
//  STX       0x02              // ctrl-B
//  ETX       0x03              // ctrl-C
//  EOT       0x04              // ctrl-D
//  ENQ       0x05              // ctrl-E
//  ACK       0x06              // ctrl-F
//  BEL       0x07              // ctrl-G
//  BS        0x08              // ctrl-H                      BS:       Cursor-Left
//  HT        0x09              // ctrl-I                      HT:       Cursor-Right
//  LF        0x0A              // ctrl-J                      LF = \n:  Home cursor and clear screen
//  VT        0x0B              // ctrl-K
//  FF        0x0C              // ctrl-L
//  CR        0x0D              // ctrl-M                      CR = \r:  Home Cursor w/ no screen clear
//  SO        0x0E              // ctrl-N
//  SI        0x0F              // ctrl-O
//  DLE       0x10              // ctrl-P
//  DC1       0x11              // ctrl-Q  (xon)               Scroll Splash Screen, Clear Screen, and Home Cursor
//  DC2       0x12              // ctrl-R
//  DC3       0x13              // ctrl-S  (xoff)
//  DC4       0x14              // ctrl-T
//  NAK       0x15              // ctrl-U                      (also  Home cursor and clear screen)
//  SYN       0x16              // ctrl-V                      (also  Home cursor w/ no screen clear)
//  ETB       0x17              // ctrl-W
//  CAN       0x18              // ctrl-X
//  EM        0x19              // ctrl-Y
//  SUB       0x1A              // ctrl-Z                      (also  Cursor-Left)
//  ESC       0x1B              // ctrl-[                      (start prog sequence?)
//  FS        0x1C              // ctrl-\
//  GS        0x1D              // ctrl-]
//  RS        0x1E              // ctrl-^
//  US        0x1F              // ctrl-(hyphen) or ctrl-_
//  DEL       0x7F

void send_FP_tx_byte(int8 c)
{
  // switch 232 to front-panel display (RCV is dummy):
  #use RS232 (FORCE_SW, DISABLE_INTS, BAUD=9600, XMIT=DISPLAY_TX, RCV=CONTROL_PB__, BITS=8, PARITY=N)

  putc(c);

  // restore normal serial port:
  #use RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, BITS=8, PARITY=N, ERRORS)
}


//--------------------------------------------------------------------------------------------
//  void read_conn_params_from_eeprom(void)         reads connect params from eeprom, and loads globals
//
//  Public routine read_conn_params_from_eeprom() reads connect params from eeprom, and loads globals

void read_conn_params_from_eeprom(void)
{
  mode = read_eeprom(INT_EE_MODE);
  port_A_bits = read_eeprom(INT_EE_PORT_A_BITS);
  port_A_port = read_eeprom(INT_EE_PORT_A_PORT);
  port_A_rate = read_eeprom(INT_EE_PORT_A_RATE);
  port_B_bits = read_eeprom(INT_EE_PORT_B_BITS);
  port_B_port = read_eeprom(INT_EE_PORT_B_PORT);
  port_B_rate = read_eeprom(INT_EE_PORT_B_RATE);
}


//--------------------------------------------------------------------------------------------
//  void write_conn_params_to_eeprom(void)          writes connect params to eeprom, from globals
//
//  Public routine write_conn_params_to_eeprom() writes connect params to eeprom, from globals

void write_conn_params_to_eeprom(void)
{
  write_eeprom(INT_EE_MODE, mode);
  write_eeprom(INT_EE_PORT_A_BITS, port_A_bits);
  write_eeprom(INT_EE_PORT_A_PORT, port_A_port);
  write_eeprom(INT_EE_PORT_A_RATE, port_A_rate);
  write_eeprom(INT_EE_PORT_B_BITS, port_B_bits);
  write_eeprom(INT_EE_PORT_B_PORT, port_B_port);
  write_eeprom(INT_EE_PORT_B_RATE, port_B_rate);
}


//--------------------------------------------------------------------------------------------
//  void read_prog_params_from_eeprom(void)         reads prog params from eeprom, and loads globals
//
//  Public routine read_prog_params_from_eeprom() reads prog params from eeprom, and loads globals

void read_prog_params_from_eeprom(void)
{
  int8 i;

  for (i=0; i<=7; ++i)
  {
    auto_crlf_str[i] = read_eeprom(INT_EE_AUTO_CRLF_STR + i);
#if (STREAM_OPTIONS)
    start_of_mssg_str[i] = read_eeprom(INT_EE_START_OF_MSSG_STR + i);
    end_of_mssg_str[i] = read_eeprom(INT_EE_END_OF_MSSG_STR + i);
    cond_code_str[i] = read_eeprom(INT_EE_COND_CODE_STR + i);
    selcal_station_str[i] = read_eeprom(INT_EE_SELCAL_STATION_STR + i);
    selcal_all_str[i] = read_eeprom(INT_EE_SELCAL_ALL_STR + i);
#endif
  }

  max_chars_per_line = read_eeprom(INT_EE_MAX_CHARS_PER_LINE);
  motor_power_mode = read_eeprom(INT_EE_MOTOR_POWER_MODE);
  prog_flags_1 = read_eeprom(INT_EE_PROG_FLAGS_1);
  prog_flags_2 = read_eeprom(INT_EE_PROG_FLAGS_2);
}


//--------------------------------------------------------------------------------------------
//  void write_prog_params_to_eeprom(void)          writes prog params to eeprom, from globals
//
//  Public routine write_prog_params_to_eeprom() writes prog params to eeprom, from globals

void write_prog_params_to_eeprom(void)
{
  int8 i;

  for (i=0; i<=7; ++i)
  {
    write_eeprom((INT_EE_AUTO_CRLF_STR + i), auto_crlf_str[i]);
#if (STREAM_OPTIONS)
    write_eeprom((INT_EE_START_OF_MSSG_STR + i), start_of_mssg_str[i]);
    write_eeprom((INT_EE_END_OF_MSSG_STR + i), end_of_mssg_str[i]);
    write_eeprom((INT_EE_COND_CODE_STR + i), cond_code_str[i]);
    write_eeprom((INT_EE_SELCAL_STATION_STR + i), selcal_station_str[i]);
    write_eeprom((INT_EE_SELCAL_ALL_STR + i), selcal_all_str[i]);
#endif
  }

  write_eeprom(INT_EE_MAX_CHARS_PER_LINE, max_chars_per_line);
  write_eeprom(INT_EE_MOTOR_POWER_MODE, motor_power_mode);
  write_eeprom(INT_EE_PROG_FLAGS_1, prog_flags_1);
  write_eeprom(INT_EE_PROG_FLAGS_2, prog_flags_2);
}


//--------------------------------------------------------------------------------------------
//  int8 port_bitrate(int8 rate)                    returns bitrate based on 60- to 100-wpm, or 110-baud
//
//  Public routine br=port_bitrate(rate) returns bitrate based on 60- to 100-wpm, or 110-baud

int8 port_bitrate(int8 rate)
{
  switch(rate)                                                                  // set to 1 less, for uart logic
  {
  case 60:                                                                      // 60 wpm = 45.45 baud = 22.0 ms bits
    return(BAUDOT_60_WPM - 1);
  case 66:                                                                      // 66 wpm = 50.00 baud = 20.0 ms bits
    return(BAUDOT_66_WPM - 1);
  case 75:                                                                      // 75 wpm = 55.55 baud = 18.0 ms bits
    return(BAUDOT_75_WPM - 1);
  case 100:                                                                     // 100 wpm = 74.07 baud = 13.5 ms bits
    return(BAUDOT_100_WPM - 1);
  case 110:                                                                     // 110 baud = 9.09 ms bits
    return(ASCII_110_BAUD - 1);
  }
  return(0);                                                                    // invalid rate
}


//--------------------------------------------------------------------------------------------
//  int8 port_input_bit(int8 port)                  returns input bit based on port number
//
//  Public routine pi=port_input_bit(port) returns input bit based on port number

int8 port_input_bit(int8 port)
{
  switch(port)
  {
  case 1:                                                                       // HV1 port
    return(HV1_IN_BIT);
  case 2:                                                                       // HV2 port
    return(HV2_IN_BIT);
  case 3:                                                                       // LV port
    return(LV_IN_BIT);
  case 4:                                                                       // TU port
    return(TU_IN_BIT);
  }
  return(0);                                                                    // invalid port
}


//--------------------------------------------------------------------------------------------
//  int8 port_output_bit(int8 port)                 returns output bit based on port number
//
//  Public routine po=port_output_bit(port) returns output bit based on port number

int8 port_output_bit(int8 port)
{
  switch(port)
  {
  case 1:                                                                       // HV1 port
    return(HV1_OUT_BIT);
  case 2:                                                                       // HV2 port
    return(HV2_OUT_BIT);
  case 3:                                                                       // LV port
    return(LV_OUT_BIT);
  case 4:                                                                       // TU port
    return(TU_OUT_BIT);
  }
  return(0);                                                                    // invalid port
}


//--------------------------------------------------------------------------------------------
//  int8 limit(int8 param, int8 min, int8 max)      limits param to min/max range
//
//  Public routine p=limit(param, min, max) limits param to min/max range

int8 limit(int8 param, int8 min, int8 max)
{
  if (param > max)  param = max;
  if (param < min)  param = min;
  return(param);
}


//--------------------------------------------------------------------------------------------
//  int8 wpm_limit(int8 wpm)                        limits wpm to 60, 66, 75, or 100
//
//  Public routine w=wpm_limit(wpm) limits wpm to 60, 66, 75, or 100

int8 wpm_limit(int8 wpm)
{
  if (wpm == 100)
    return(100);

  if (wpm == 75)
    return(75);

  if (wpm == 66)
    return(66);

  // else use 60-wpm:
  return(60);
}


//--------------------------------------------------------------------------------------------
//  void change_output_signal(int8 out, int8 sta)   changes state of output pin
//
//  Public routine change_output_signal(out, sta) changes state of pin out, to new state sta.
//  Message is sent to update status (or error if bad out param).
//  Global MOT_port_image is updated before output.

void change_output_signal(int8 out, int8 sta)
{
  int8 port_bit;

  sta = limit(sta, 0, 1);

  // --- which output pin?
  switch(out)
  {
  case 1:                                                                       // HV1 loop motor (1 = on)
    port_bit = HV1_MOT_BIT;
    break;
  case 2:                                                                       // HV2 loop motor (1 = on)
    port_bit = HV2_MOT_BIT;
    break;
  case 3:                                                                       // LV  loop motor (1 = on)
    port_bit = LV_MOT_BIT;
    break;
  case 4:                                                                       // Push-To-Talk output (on TU-232 jack) (1 = on)
    port_bit = PTT_BIT;
    break;
  default:                                                                      // invalid out param -- send error message
    //  --- send  -.TC,240,1,err_id <cr> error message:
    vp_msg[0] = 240;                                                            // mssg-id = error mssg
    vp_msg[1] = 1;                                                              // num params
    vp_msg[2] = ERR_CMD_PARAM;
    send_tc_mssg_params(3);                                                     // send mssg and flash status led
    return;
  }

  // --- update global and output to port:
  if (sta)                                                                      // sta:  0=off, 1=on
    MOT_port_image |= port_bit;
  else
    MOT_port_image &= ~port_bit;

  PORT_A = MOT_port_image;


  // --- send  -.TC,20,2,out,sta <cr>  status message:
  vp_msg[0] = 20;                                                               // mssg-id
  vp_msg[1] = 2;                                                                // num params
  vp_msg[2] = out;
  vp_msg[3] = sta;
  send_tc_mssg_params(4);                                                       // send mssg and flash status led
}


//--------------------------------------------------------------------------------------------
//  int8 query_output_signal(int8 out)              returns state of output pin
//
//  Public routine s=query_output_signal(out) returns state of pin out from global MOT_port_image.

int8 query_output_signal(int8 out)
{
  int8 port_bit;

  // --- which output pin?
  switch(out)
  {
  case 1:                                                                       // HV1 loop motor (1 = on)
    port_bit = HV1_MOT_BIT;
    break;
  case 2:                                                                       // HV2 loop motor (1 = on)
    port_bit = HV2_MOT_BIT;
    break;
  case 3:                                                                       // LV  loop motor (1 = on)
    port_bit = LV_MOT_BIT;
    break;
  case 4:                                                                       // Push-To-Talk output (on TU-232 jack) (1 = on)
    port_bit = PTT_BIT;
    break;
  default:                                                                      // invalid out param -- return 255
    return(255);
  }

  // --- return state of out pin (0=off, 1=on):
  if (MOT_port_image & port_bit)
    return (1);
  else
    return (0);
}


//--------------------------------------------------------------------------------------------
//  int8 control_pb_pressed(void)                   if CONTROL PB pressed/released, return TRUE
//
//  Public routine b=control_pb_pressed() checks CONTROL pushbutton, and if it has been
//  pressed and released, returns TRUE (else FALSE).

int8 control_pb_pressed(void)
{
  if (!input(CONTROL_PB__))                         // if pin low,
  {
    delay_ms (30);                                  // delay for debounce
    if (!input(CONTROL_PB__))                       // pin is still low
    {
      while (!input(CONTROL_PB__))                  // wait for button release
        restart_wdt();
      return(TRUE);                                 // valid press/release
    }
  }
  return(FALSE);                                    // invalid press/release
}


//--------------------------------------------------------------------------------------------
//  char ustty_to_ascii(char c)                     converts USTTY-Baudot to ASCII-67
//
//  Public routine c=ustty_to_ascii(c) converts USTTY-Baudot to ASCII-67.
//  Keeps track of incoming LTRS/FIGS case in global baudot_in_current_case (presumes LTRS initially).
//  Returns NUL for BLANK.
//  Optionally returns SO for FIGS, and SI for LTRS (after updating global baudot_in_current_case).
//
//        USTTY (aka baudot)                  ASCII translation
//    ---------------------------           ---------------------
//    hex   binary    LTRS   FIGS              LTRS        FIGS
//    ----  -----    -----  -----     |     ---------   ---------
//    0x00  00000    BLANK  BLANK     |     0x00  NUL   0x00  NUL
//    0x01  00001      E      3       |     0x45  E     0x33  3
//    0x02  00010      LF     LF      |     0x0A  LF    0x0A  LF
//    0x03  00011      A      -       |     0x41  A     0x2D  -
//    0x04  00100      SP     SP      |     0x20  SP    0x20  SP
//    0x05  00101      S     BELL     |     0x53  S     0x07  BEL
//    0x06  00110      I      8       |     0x49  I     0x38  8
//    0x07  00111      U      7       |     0x55  U     0x37  7
//    0x08  01000      CR     CR      |     0x0D  CR    0x0D  CR
//    0x09  01001      D      $       |     0x44  D     0x24  $
//    0x0A  01010      R      4       |     0x52  R     0x34  4
//    0x0B  01011      J      '       |     0x4A  J     0x27  '
//    0x0C  01100      N      ,       |     0x4E  N     0x2C  ,
//    0x0D  01101      F      !       |     0x46  F     0x21  !
//    0x0E  01110      C      :       |     0x43  C     0x3A  :
//    0x0F  01111      K      (       |     0x4B  K     0x28  (
//    0x10  10000      T      5       |     0x54  T     0x35  5
//    0x11  10001      Z      "       |     0x5A  Z     0x22  "
//    0x12  10010      L      )       |     0x4C  L     0x29  )
//    0x13  10011      W      2       |     0x57  W     0x32  2
//    0x14  10100      H      #       |     0x48  H     0x23  #
//    0x15  10101      Y      6       |     0x59  Y     0x36  6
//    0x16  10110      P      0       |     0x50  P     0x30  0
//    0x17  10111      Q      1       |     0x51  Q     0x31  1
//    0x18  11000      O      9       |     0x4F  O     0x39  9
//    0x19  11001      B      ?       |     0x42  B     0x3F  ?
//    0x1A  11010      G      &       |     0x47  G     0x26  &
//    0x1B  11011     FIGS   FIGS     |     0x0E  SO    0x0E  SO     (SI/SO is optional)
//    0x1C  11100      M      .       |     0x4D  M     0x2E  .
//    0x1D  11101      X      /       |     0x58  X     0x2F  /
//    0x1E  11110      V      ;       |     0x56  V     0x3B  ;
//    0x1F  11111     LTRS   LTRS     |     0x0F  SI    0x0F  SI     (SI/SO is optional)


// ----- ustty to ascii-67 translation table:
//          Contains 64 ascii characters in ustty order.
//          First 32 chars are for LTRS case (ascii = ustty_to_asc_table[ustty])
//          Upper 32 chars are for FIGS case (ascii = ustty_to_asc_table[ustty + 32])
int8 const ustty_to_ascii_table[64] =
  {
    NUL, 'E',  LF, 'A', ' ', 'S', 'I', 'U',  CR, 'D', 'R', 'J',  'N', 'F', 'C', 'K',
		'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q', 'O', 'B', 'G',  SO,  'M', 'X', 'V',  SI,
    NUL, '3',  LF, '-', ' ', BEL, '8', '7',  CR, '$', '4', '\'', ',', '!', ':', '(',
		'5', '"', ')', '2', '#', '6', '0', '1', '9', '?', '&',  SO,  '.', '/', ';',  SI
  };


char ustty_to_ascii(char c)
{

  // check for case shifting:
  switch (c)
  {
    case LTRS:
      baudot_in_current_case = 0;                                               // subsequent chars from LTRS case
      if (!si_so_for_ltrs_figs)
        c = 0;                                                                  // null char to trap out SI
      break;

    case FIGS:
      baudot_in_current_case = 32;                                              // subsequent chars from FIGS case
      if (!si_so_for_ltrs_figs)
        c = 0;                                                                  // null char to trap out SO
      break;

//    case BSP:
//      baudot_in_current_case = 0;                                             // on space, shift down to LTRS
//      break;

//    case BCR:
//      baudot_in_current_case = 0;                                             // on cr, shift down to LTRS
//      break;
  }

  // translate USTTY character to ASCII-67:
  return (ustty_to_ascii_table[c + baudot_in_current_case]);
}


//--------------------------------------------------------------------------------------------
//  char ascii_to_ustty(char c)                     converts ASCII-67 to USTTY-Baudot
//
//  Public routine c=ascii_to_ustty(c) converts ASCII-67 to USTTY-Baudot.
//  Except for a few control chars, only the chars from SP to Z are mapped.
//  Returns BLANK for NUL, and for unmapped chars.
//  Keeps track of outgoing LTRS/FIGS case in global baudot_out_current_case.
//  Returns FIGS for SO (^N), and LTRS for SI (^O), for testing or special use (note that
//  needed FIGS/LTRS chars are sent automatically -- SO/SI are only used for inserting extras).
//  Calling routine to check baudot_out_new_case and send it if non-zero (FIGS or LTRS),
//  then send returned USTTY char.
//
//            ASCII-67                                        USTTY translation
//  -----------------------------         -----------------------------------------------------
//  col-1   col-2   col-3   col-4            col-1         col-2         col-3         col-4
//  ------  -----   -----   -----         -----------   -----------   -----------   -----------
//  ^@ NUL    SP      @       `      |    N 0x00        N 0x04  SP    F 0x00        N 0x00
//  ^A SOH    !       A       a      |    N 0x00        F 0x0D  !     L 0x03  A     L 0x03  A
//  ^B STX    "       B       b      |    N 0x00        F 0x11  "     L 0x19  B     L 0x19  B
//  ^C ETX    #       C       c      |    N 0x00        F 0x14  #     L 0x0E  C     L 0x0E  C
//  ^D EOT    $       D       d      |    N 0x00        F 0x09  $     L 0x09  D     L 0x09  D
//  ^E ENQ    %       E       e      |    N 0x00        F 0x00        L 0x01  E     L 0x01  E
//  ^F ACK    &       F       f      |    N 0x00        F 0x1A  &     L 0x0D  F     L 0x0D  F
//  ^G BEL    '       G       g      |    F 0x05 BELL   F 0x0B  '     L 0x1A  G     L 0x1A  G
//  ^H BS     (       H       h      |    N 0x00        F 0x0F  (     L 0x14  H     L 0x14  H
//  ^I HT     )       I       i      |    N 0x00        F 0x12  )     L 0x06  I     L 0x06  I
//  ^J LF     *       J       j      |    N 0x02  LF    F 0x00        L 0x0B  J     L 0x0B  J
//  ^K VT     +       K       k      |    N 0x00        F 0x00        L 0x0F  K     L 0x0F  K
//  ^L FF     ,       L       l      |    N 0x00        F 0x0C  ,     L 0x12  L     L 0x12  L
//  ^M CR     -       M       m      |    N 0x08  CR    F 0x03  -     L 0x1C  M     L 0x1C  M
//  ^N SO     .       N       n      |    N 0x1B FIGS   F 0x1C  .     L 0x0C  N     L 0x0C  N
//  ^O SI     /       O       o      |    N 0x1F LTRS   F 0x1D  /     L 0x18  O     L 0x18  O
//  ^P DLE    0       P       p      |    N 0x00        F 0x16  0     L 0x16  P     L 0x16  P
//  ^Q DC1    1       Q       q      |    N 0x00        F 0x17  1     L 0x17  Q     L 0x17  Q
//  ^R DC2    2       R       r      |    N 0x00        F 0x13  2     L 0x0A  R     L 0x0A  R
//  ^S DC3    3       S       s      |    N 0x00        F 0x01  3     L 0x05  S     L 0x05  S
//  ^T DC4    4       T       t      |    N 0x00        F 0x0A  4     L 0x10  T     L 0x10  T
//  ^U NAK    5       U       u      |    N 0x00        F 0x10  5     L 0x07  U     L 0x07  U
//  ^V SYN    6       V       v      |    N 0x00        F 0x15  6     L 0x1E  V     L 0x1E  V
//  ^W ETB    7       W       w      |    N 0x00        F 0x07  7     L 0x13  W     L 0x13  W
//  ^X CAN    8       X       x      |    N 0x00        F 0x06  8     L 0x1D  X     L 0x1D  X
//  ^Y EM     9       Y       y      |    N 0x00        F 0x18  9     L 0x15  Y     L 0x15  Y
//  ^Z SUB    :       Z       z      |    N 0x00        F 0x0E  :     L 0x11  Z     L 0x11  Z
//  ^[ ESC    ;       [       {      |    N 0x00        F 0x1E  ;     N 0x00        N 0x00
//  ^\ FS     <       \       |      |    N 0x00        F 0x00        N 0x00        N 0x00
//  ^] GS     =       ]       }      |    N 0x00        F 0x00        N 0x00        N 0x00
//  ^^ RS     >       ^       ~      |    N 0x00        F 0x00        N 0x00        N 0x00
//  ^_ US     ?       _      DEL     |    N 0x00        F 0x19  ?     N 0x00        N 0x00
//
//      In col-2/3, there are some BLANKS sent with a FIGS shift -- this is due to running them
//      through map table instead of trapping them out.  Not a problem since LTRS will be sent
//      if next char is alpha.


// ----- ascii-67 to ustty translation table:
//          Contains 59 ustty characters in modified-ascii order.
//          Table starts at ascii SPACE, but ustty BELL is stored here to keep figs together.
//          First 33 chars are for FIGS case (through @)
//          Upper 26 chars are for LTRS case (A to Z)
int8 const ascii_to_ustty_table[59] =
  {
    0x05,0x0D,0x11,0x14,0x09,0x00,0x1A,0x0B,0x0F,0x12,0x00,0x00,0x0C,0x03,0x1C,0x1D,
    0x16,0x17,0x13,0x01,0x0A,0x10,0x15,0x07,0x06,0x18,0x0E,0x1E,0x00,0x00,0x00,0x19,
    0x00,0x03,0x19,0x0E,0x09,0x01,0x0D,0x1A,0x14,0x06,0x0B,0x0F,0x12,0x1C,0x0C,0x18,
    0x16,0x17,0x0A,0x05,0x10,0x07,0x1E,0x13,0x1D,0x15,0x11
  };


char ascii_to_ustty (char c)
{
  baudot_out_new_case = 0;                                                      // presume no case change initially

  // ----- check for special chars:
  switch (c)
  {
    case LF:
      return(BLF);                                                              // no case change, send BLF
    case CR:
      return(BCR);                                                              // no case change, send BCR
    case SPACE:
      return(BSP);                                                              // no case change, send BSP
    case SO:
      return(FIGS);                                                             // no case change, send FIGS (for testing)
    case SI:
      return(LTRS);                                                             // no case change, send LTRS (for testing)
    case BEL:
      c = SPACE;                                                                // BELL code in SP position in table
      break;
  }

  // ----- change ascii chars lowercase to uppercase:
  c = toupper(c);

  // ----- check for ltrs/figs case change, and translate char:
  if ((c >= ' ') && (c <= 'Z'))                                                 // ignore chars except SP to Z
  {
    if (c < 'A')                                                                // below A are only figs chars
    {
      // this is a figs char -- if previous case was not figs, need to update:
      if (baudot_out_current_case != FIGS)
      {
        baudot_out_current_case = FIGS;
        baudot_out_new_case = FIGS;
      }
    }
    else                                                                        // A and above are ltrs
    {
      // this is a ltrs char -- if previous case was not ltrs, need to update:
      if (baudot_out_current_case != LTRS)
      {
        baudot_out_current_case = LTRS;
        baudot_out_new_case = LTRS;
      }
    }

    // translate ASCII-67 character to USTTY:
    return (ascii_to_ustty_table[c - SPACE]);                                   // table starts at SP (now actually BEL)
  }
  else
    return (BLANK);                                                             // all others get BLANK, including
                                                                                //  ascii NUL (and no case change)
}


//--------------------------------------------------------------------------------------------
//  void rx_options_and_send_PB_out_char(char c, int8 format)     baudot/ascii output char piped through rx stream options
//
//  Public routine rx_options_and_send_PB_out_char(c, format) gets baudot/ascii output char, and filters it
//  through rx stream options before output to tty-port B (tty-rx).
//  For BAUDOT format, detects stings for Start-of-Message, End-of-Message, Conditioning-Code, SelCal-Station,
//  SelCal-All, and WRU.  Provides autostart motor powering, auto-crlf insertion, unshifts, and rx-diddle filter.
//  Also keeps track of outgoing LTRS/FIGS case.
//  For ASCII format, provides motor powering for autostart-char (only), and auto-crlf insertion.
//  Uses a shitload of globals.

void rx_options_and_send_PB_out_char(char c, int8 format)
{
  int8 i;


  // ========== reset motor-power-off timer ==========
  //////////t2_int_count = T2_INTS_PER_SECOND;
  ///////seconds = 0;
  ///////minutes = 0;


  switch (format)
  {

  // ========== RX-Options for Baudot stream ==========
  case BAUDOT:
    if (c)                                                                      // only check for rx-options if char is not BLANK
    {

#if (STREAM_OPTIONS)
      // ---------- Start-of-Message string detection (SP SP SP SP SP CR CR LF by default, in baudot) ----------
      //
      if (!got_start_of_mssg_match)
      {
        // --- does current rx char match next char in string?
        if (c == start_of_mssg_str[start_of_mssg_matches])
          ++start_of_mssg_matches;

        // --- did we get all 8 chars in string to match?
        if (start_of_mssg_matches == 8)
          got_start_of_mssg_match = 1;

        // --- did we hit a null in the string before 8 chars?
        if ( (start_of_mssg_matches != 0) && (start_of_mssg_str[start_of_mssg_matches] == 0) )
          got_start_of_mssg_match = 1;
      }


      // ---------- End-of-Message string detection (LF N N N N by default, in baudot) ----------
      //
      if (!got_end_of_mssg_match)
      {
        // --- does current rx char match next char in string?
        if (c == end_of_mssg_str[end_of_mssg_matches])
          ++end_of_mssg_matches;

        // --- did we get all 8 chars in string to match?
        if (end_of_mssg_matches == 8)
          got_end_of_mssg_match = 1;

        // --- did we hit a null in the string before 8 chars?
        if ( (end_of_mssg_matches != 0) && (end_of_mssg_str[end_of_mssg_matches] == 0) )
          got_end_of_mssg_match = 1;
      }


      // ---------- Conditioning-Code string detection (SP SP SP SP SP CR CR LF by default, in baudot) ----------
      //
      if (!got_cond_code_match)
      {
        // --- does current rx char match next char in string?
        if (c == cond_code_str[cond_code_matches])
          ++cond_code_matches;

        // --- did we get all 8 chars in string to match?
        if (cond_code_matches == 8)
          got_cond_code_match = 1;

        // --- did we hit a null in the string before 8 chars?
        if ( (cond_code_matches != 0) && (cond_code_str[cond_code_matches] == 0) )
          got_cond_code_match = 1;
      }


      // ---------- SelCal-Station string detection (T T Y C O N N by default, in baudot) ----------
      //
      if (!got_selcal_station_match)
      {
        // --- does current rx char match next char in string?
        if (c == selcal_station_str[selcal_station_matches])
          ++selcal_station_matches;

        // --- did we get all 8 chars in string to match?
        if (selcal_station_matches == 8)
          got_selcal_station_match = 1;

        // --- did we hit a null in the string before 8 chars?
        if ( (selcal_station_matches != 0) && (selcal_station_str[selcal_station_matches] == 0) )
          got_selcal_station_match = 1;
      }


      // ---------- SelCal-All string detection (Z C Z C by default, in baudot) ----------
      //
      if (!got_selcal_all_match)
      {
        // --- does current rx char match next char in string?
        if (c == selcal_all_str[selcal_all_matches])
          ++selcal_all_matches;

        // --- did we get all 8 chars in string to match?
        if (selcal_all_matches == 8)
          got_selcal_all_match = 1;

        // --- did we hit a null in the string before 8 chars?
        if ( (selcal_all_matches != 0) && (selcal_all_str[selcal_all_matches] == 0) )
          got_selcal_all_match = 1;
      }
#endif


      // ---------- motor-powering (autostart and selcal) ----------
      switch (motor_power_mode)
      {

      // --- sense any char to turn motor on (timeout to turn off?):
      case MOT_PWR_AUTOSTART_CHAR:
        if (query_output_signal(port_B_port) == 0)                              // if motor is off, turn on
          change_output_signal(port_B_port, 1);
        break;


#if (STREAM_OPTIONS)
      // --- sense start-of-mssg string to turn motor on, end-of-mssg string to turn off:
      case MOT_PWR_AUTOSTART_STRING:

        // --- motor on if got start_of_mssg_str match:
        if (got_start_of_mssg_match)
        {
          got_start_of_mssg_match = 0;
          start_of_mssg_matches = 0;
          change_output_signal(port_B_port, 1);
        }

        // --- motor off if got end_of_mssg_str match:
        if (got_end_of_mssg_match)
        {
          got_end_of_mssg_match = 0;
          end_of_mssg_matches = 0;
          change_output_signal(port_B_port, 0);
        }
        break;
#endif


      // --- special sense (transitions?) to turn motor on (timeout to turn off?):
      case MOT_PWR_AUTOSTART_SPECIAL:
        break;


#if (STREAM_OPTIONS)
      // --- selcal-station, selcal-all, and both:
      case MOT_PWR_SELCAL_STATION:
      case MOT_PWR_SELCAL_ALL:
      case MOT_PWR_SELCAL_BOTH:

        // --- motor on if got cond_code_str and selcal_station_str match:
        if (motor_power_mode != MOT_PWR_SELCAL_ALL)
        {
          if (got_cond_code_match && got_selcal_station_match)
          {
            got_cond_code_match = 0;
            cond_code_matches = 0;
            got_selcal_station_match = 0;
            selcal_station_matches = 0;
            change_output_signal(port_B_port, 1);
          }
        }

        // --- motor on if got cond_code_str and selcal_all_str match:
        if (motor_power_mode != MOT_PWR_SELCAL_STATION)
        {
          if (got_cond_code_match && got_selcal_all_match)
          {
            got_cond_code_match = 0;
            cond_code_matches = 0;
            got_selcal_all_match = 0;
            selcal_all_matches = 0;
            change_output_signal(port_B_port, 1);
          }
        }

        // --- motor off if got end_of_mssg_str match:
        if (got_end_of_mssg_match)
        {
          got_end_of_mssg_match = 0;
          end_of_mssg_matches = 0;
          change_output_signal(port_B_port, 0);
        }
        break;
#endif


      // --- should not get here, but ignore it if we do:
      default:
        break;
      }


      // ---------- WRU (Who-Are-You) string detection (selcal-station string plus figs-H) ----------
      //
    ////  if (wru_reply && !got_wru_match)
    ////  {
    //// flag that selcal-stn was rcvd, then look for figs mode and H (in one or two chars only, else ignore)
    //// PTT signal is activated for enabling a transmitter, the WRU-Reply string is sent, and then PTT is deactivated.
    ////  }


      // ---------- auto-crlf insertion, if reaching end of line ----------
      if (rx_auto_crlf_insert)
      {
        switch (c)
        {
        case BCR:                                                               // baudot cr, so reset counter
          rx_cr_char_count = 0;
          break;

        case FIGS:                                                              // don't count case shift
        case LTRS:                                                              // don't count case shift
        case BLF:                                                               // don't count linefeed
          break;

        default:
          ++rx_cr_char_count;                                                   // other chars increment counter
          break;
        }

        if (rx_cr_char_count >= max_chars_per_line)                             // hit limit, so...
        {
          rx_cr_char_count = 0;
          for (i=0; i<=7; i++)                                                  // ...insert auto_crlf_str string into buffer
          {
            if (auto_crlf_str[i])                                               // (don't send 0 = BLANK)
              send_PB_out_char(auto_crlf_str[i]);
          }
        }
      }


      // ---------- unshifts, and rx-diddle filter ----------
      //
      // --- if currently in FIGS mode:
      if (baudot_out_current_case == FIGS)
      {
        // --- unshift on baudot space?
        if (rx_unshift_on_space && (c == BSP) )
        {
           baudot_out_current_case = LTRS;
           send_PB_out_char(LTRS);                                              // LTRS will be inserted before BSP
        }

        // --- unshift on baudot cr?
        if (rx_unshift_on_cr && (c == BCR) )
        {
           baudot_out_current_case = LTRS;
           send_PB_out_char(LTRS);                                              // LTRS will be inserted before BCR
        }

        // --- if rx diddle filter is on, just return to discard extra FIGS:
        if (rx_diddle_filter && (c == FIGS) )
          return;
      }
      // --- if currently in LTRS mode:
      else
      {
        // --- if rx diddle filter is on, just return to discard extra LTRS:
        if (rx_diddle_filter && (c == LTRS) )
          return;
      }

      // ---------- update current figs/ltrs state (for use next time through) ----------
      //
      if (c == FIGS)
        baudot_out_current_case = FIGS;
      if (c == LTRS)
        baudot_out_current_case = LTRS;

    }
    break;


  // ========== RX-Options for Ascii stream ==========
  case ASCII:
    if (c)                                                                      // only check for rx-options if char is not NUL
    {
      // ---------- motor-powering (autostart-char only) ----------
      switch (motor_power_mode)
      {
      // --- sense any char to turn motor on (timeout to turn off?):
      case MOT_PWR_AUTOSTART_CHAR:
        if (query_output_signal(port_B_port) == 0)                              // if motor is off, turn on
          change_output_signal(port_B_port, 1);
        break;
      }

      // ---------- auto-crlf insertion, if reaching end of line ----------
      if (rx_auto_crlf_insert)
      {
        if (c == CR)                                                            // ascii cr, so reset counter
          rx_cr_char_count = 0;
        else
          ++rx_cr_char_count;

        if (rx_cr_char_count >= max_chars_per_line)                             // hit limit, so...
        {
          rx_cr_char_count = 0;
          send_PB_out_char(CR);                                                 // send CR
          send_PB_out_char(LF);                                                 // send LF
          for (i=0; i<=3; i++)                                                  // send 3 NULs
            send_PB_out_char(NUL);
        }
      }

    }
    break;

  }


  // ========== send char to port B ==========
  send_PB_out_char(c);
}



