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


//============================================================================================
//
//  This is the main code for the TTY-CONNECT controller, a 20 MHz PIC 18F2620.
//
//  Serial i/o for the sci (usart) is handled in tc_sci_isr.c, which provides buffered
//  i/o in TBE_isr() and RDA_isr(), as well as interface routines for the main program.
//  The rx routine provides a 500-byte buffer, and XON/XOFF flow control.
//  The tx routine provides a 300-byte buffer, and no flow control.
//  Serial port:  38.4 Kbaud, 8 data bits, no parity, 1 stop, XonXoff flow control (rx only).
//
//  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
//
//  Timer-2 is an 8-ms interrupt handler that provides time/date counting,
//  and seconds-time-out for motor-turn-off feature.
//
//  This chip is an I2C master, connected to external eeprom, using FAST (400KHz) I2C.
//  External eeprom holds demo pages of text for sending to tty loops.
//
//============================================================================================
//
//      to do:
//          - test wdt (add TEST-PB and hold to test)

//          - #bit          got_wru_match             = flags_1.5
//          - static int8   auto_crlf_nul_pads;   // num nulls added after auto-crlf in ascii mode

//          - init baudot port with cr-lf-ltrs, ascii port with cr-lf
//          - odd char after each connect?
//          - timeouts for motor off ???  prog?
//          - STREAM_OPTIONS: baudot string stuff (selcal...) in tc_cmds, tc_subs, and tc_main
//          - wru
//          - test messages (ext eeprom?)
//          - ability to tx/rx BLANK and NUL
//          - prog num nuls for M33 (rx_options_...)
//          - prog char if tx_diddle (timer-1)
//          - check for char loss from pc to tty with large streams (sci rx buffer)?
//          - local echo option for full-dup?
//          - CRLF   0xDA    // special code for inserting both CR (0x0D) and LF (0x0A) ?
//          - send break?
//          - open/close tty line?
//          - autostart-special?
//
//============================================================================================
//
//      revision summary:
//
//      ver 2.2   (x/x/08):
//          - changed chip to 18F2620 (was 18F252)
//          - increased PC232 port buffers:  rx now 500 bytes, tx now 300 bytes
//                (were 300 bytes rx and 200 bytes tx in public ver 2.0 using 18F252)
//                (were 80 bytes rx and 16 bytes tx in public ver 1.0 using 16F876)
//          - increased tty port buffers (Atx, Arx, Btx, Brx) to 250-bytes each
//                (were 150 bytes each in public ver 2.0 using 18F252)
//                (were 16 bytes each in public ver 1.0 using 16F876)
//          - added code for driving front-panel serial VFD display
//          - added clock functions for timestamping or general tty-clock use
//          - added large external eeprom for storing demo messages to print on tty
//
//      ver 2.1   (7/15/07):
//          - not public
//
//      ver 2.0   (6/20/06):
//          - changed chip to 18F252 (was 16F876)
//          - increased PC232 port buffers:  rx now 300-bytes (was 80), and tx now 200-bytes (was 16)
//          - increased tty port buffers (Atx, Arx, Btx, Brx) to 150-bytes each (were 16 each)
//
//      ver 1.1   (9/x/04):
//          - not public
//          - sending connection mssg to TTY-port-B
//
//============================================================================================
//
//      tc_main.c           ver 2.2     (7/x/08)
//          - changed chip to 18F2620 (was 18F252)
//          - added large external eeprom for storing demo messages to print on tty
//
//      tc_main.c           ver 2.1     (7/15/07)
//          - not public
//          - created tc_clock.c from tc_port_io.c (pulled out timer-2 intr)
//          - giving up on code-intensive lcd menu stuff
//
//      tc_main.c           ver 2.0     (6/20/06)
//          - changed chip to 18F252 (was 16F876)
//          - now using PCWH compiler version 3.235
//          - defined SCI_RXBUF_SIZE... for a 300-byte rx232 buffer (was 80-byte),
//              and 200-byte tx buffer (was 16-byte)
//          - removed RESTART_WDT from #use I2C... line
//              (this will allow a watchdog reset if I2C_READ ever hangs)
//          - replaced byte (was typedef) with int8
//          - replaced HOFFNET_STUFF with STREAM_OPTIONS
//          - added tc_lcd_i2c.c (lcd code not yet active)
//
//      tc_main.c           ver 1.1     (9/x/04)
//          - not public
//          - now sending connection mssg to TTY-port-B
//          - LCD tests
//
//      tc_main.c           ver 1.0     (9/8/04)
//          - created from tty232.c and some lattis code
//
//============================================================================================
//  Port from 16F876 to 18F252 to 18F2620
//  -------------------------------------
//
//  Main features:
//
//     Device    Program    Data RAM   EEPROM     Stack     Max-Freq                    Each, qty 100
//               (words)    (bytes)    (bytes)   (levels)                                  (2008)
//     --------------------------------------------------------------------------------------------
//     16F876       8K        368       256          8       20-MHz    (std flash)         $5.25
//     18F252      16K       1536       256         31       40-MHz    (std flash)         $5.15
//     18F2520     16K       1536       256         31       40-MHz    (enhanced flash)    $3.79
//     18F2525     24K       3968      1024         31       40-MHz    (enhanced flash)    $4.29
//     18F2620     32K       3968      1024         31       40-MHz    (enhanced flash)    $4.80
//
//  Changes:
//             - for 18F2620.h, now:  PORT_A=0xF80, PORT_B=0xF81, PORTC=0xF82
//               for 18F252.h, was:   PORT_A=0xF80, PORT_B=0xF81, PORTC=0xF82
//               for 16F876.h, was:   PORT_A=5, PORT_B=6, PORTC=7
//
//              - WDT period:    for 18F2620, now:  #fuses WDT256 in main setup area (WDT512 = 2048ms, WDT256 = 1024ms)
//                                                   (wdt divider:  WDT1=4ms, WDT2=8ms, WDT4=16ms, WDT8=32ms, WDT16=64ms, WDT32=128ms...WDT32768=131.072s)
//                               for 18F252, was:   #fuses WDT64 in main setup area (WDT64=1152ms)
//                                                   (wdt divider:  WDT1=18ms, WDT2=36ms, WDT4=72ms, WDT8=144ms, WDT16=288ms, WDT32=576ms, WDT64=1152ms, WDT128=2304)
//                               for 16F876, was:   setup_wdt(WDT_18MS) in main_init()
//                                                   (WDT_18MS, WDT_36MS, WDT_72MS, WDT_144MS,WDT_288MS, WDT_576MS, WDT_1152MS, WDT_2304MS)
//                                                   or older style: setup_counters(RTCC_INTERNAL, WDT_18MS)
//
//              - WDT enable:    for 18F2620, now:  setup_wdt(WDT_OFF) (or WDT_ON) in main_init()
//                               for 18F252, was:   setup_wdt(WDT_OFF) (or WDT_ON) in main_init()
//                               for 16F876, was:   #fuses NOWDT (or WDT)  in main setup area
//
//============================================================================================


// ----- device info:
#include    "18F2620.h"
#device     *=16                                    // use 16-bit pointers for full ram
#device     ADC=10                                  // ADC reads return 10 bits
#id         CHECKSUM
#fuses      HS, NOPROTECT, PUT, BROWNOUT, NOLVP
#fuses      WDT512                                  // WDT512 = 2048ms
#use        DELAY (CLOCK=20000000, RESTART_WDT)     // 20 MHz crystal
//
// Fuse info from 18F2620.h header file:
//////// Program memory: 32768x16  Data RAM: 4096  Stack: 31
//////// I/O: 25   Analog Pins: 10
//////// Data EEPROM: 1024
//////// C Scratch area: 00   ID Location: 2000
//////// Fuses: LP,XT,HS,RC,EC,EC_IO,H4,RC_IO,PROTECT,NOPROTECT,IESO,NOIESO
//////// Fuses: NOBROWNOUT,BROWNOUT,WDT1,WDT2,WDT4,WDT8,WDT16,WDT32,WDT64
//////// Fuses: WDT128,WDT,NOWDT,BORV21,BORV28,BORV43,BORV46,PUT,NOPUT,CPD
//////// Fuses: NOCPD,NOSTVREN,STVREN,NODEBUG,DEBUG,NOLVP,LVP,WRT,NOWRT,WRTD
//////// Fuses: NOWRTD,EBTR,NOEBTR,CPB,NOCPB,EBTRB,NOEBTRB,CCP2B3,CCP2C1
//////// Fuses: WRTC,NOWRTC,WRTB,NOWRTB,INTRC_IO,INTRC,FCMEN,NOFCMEN
//////// Fuses: BROWNOUT_SW,BROWNOUT_NOSL,WDT256,WDT512,WDT1024,WDT2048
//////// Fuses: WDT4096,WDT8192,WDT16384,WDT32768,XINST,NOXINST,PBADEN
//////// Fuses: NOPBADEN,LPT1OSC,NOLPT1OSC,MCLR,NOMCLR
////////
//////// WDT divider:  WDT1=4ms, WDT2=8ms, WDT4=16ms, WDT8=32ms, WDT16=64ms, WDT32=128ms, WDT64=256ms,
////////               WDT128=512ms, WDT256=1024ms, WDT512=2048ms...WDT32768=131.072s)
////////
//////// NOTE:  Bootloaders do not program fuse (config) settings, so bootloader code
////////        (which is initially programmed into chip) must have the fuses set properly!




// ----- define port A pins (for hard-coded i/o):
#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         P1A3                PIN_A3          // NOT USED?
#define           T2_INT_MON          PIN_A3        // optional: monitors time in timer-2 isr
#define         P1A4                PIN_A4          // NOT USED? (open-drain out)
#define         PTT                 PIN_A5          // Push-To-Talk output (on TU-232 jack) (1 = on)

// ----- define port A pins (for masking):
#define         LV_MOT_BIT          0x01            // LV  loop motor (1 = on)
#define         HV1_MOT_BIT         0x02            // HV1 loop motor (1 = on)
#define         HV2_MOT_BIT         0x04            // HV2 loop motor (1 = on)
#define         P1A3_BIT            0x08            // NOT USED?
#define         P1A4_BIT            0x10            // NOT USED? (open-drain out)
#define         PTT_BIT             0x20            // Push-To-Talk output (on TU-232 jack) (1 = on)

#define         ALL_MOTORS_ON       0x07            // all on
#define         ALL_MOTORS_OFF      0x00            // all off

#use STANDARD_IO (A)


// ----- define port B pins (for hard-coded i/o -- not used here):
#define         HV1_IN              PIN_B0          // In (loop-sense) from HV1 loop
#define         HV1_OUT             PIN_B1          // Out (loop-drive) to HV1 loop
#define         TU_IN               PIN_B2          // In from TU-232 port (txd)
#define         TU_OUT              PIN_B3          // Out to TU-232 port (rxd)
#define         LV_IN               PIN_B4          // In (loop-sense) from LV loop
#define         LV_OUT              PIN_B5          // Out (loop-drive) to LV loop
#define         HV2_IN              PIN_B6          // In (loop-sense) from HV2 loop
#define         HV2_OUT             PIN_B7          // Out (loop-drive) to HV2 loop

// ----- define port B pins (for changing i/o def passed to isr):
#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

#define         ALL_LOOPS_MARK_TU_MARK    0xAA      // three tty loops and TU port marking
#define         ALL_LOOPS_SPACE_TU_MARK   0x08      // three tty loops to space; TU marking

#use FIXED_IO (B_OUTPUTS=PIN_B1, PIN_B3, PIN_B5, PIN_B7)  // can't use standard_io, since isr does input_b()


// ----- define port C pins:
#define         CONTROL_PB__        PIN_C0          // CONTROL pushbutton (active-low-input)
#define         STATUS_LED          PIN_C1          // STATUS led (active-high-output)
#define           SCI_RX_XOFF         PIN_C1        // optional: HW handshake line (or monitor xon/xoff)
#define         MOTOR               PIN_C2          // general "all-motors" signal (active-high-output)
//#define           T1_INT_MON          PIN_C2        // optional: monitors time in timer-1 isr
#define         I2C_SCL             PIN_C3          // I2C: clock line
#define         I2C_SDA             PIN_C4          // I2C: data line
#define         DISPLAY_TX          PIN_C5          // front-panel display serial data (init high)
//#define           SCI_INT_MON         PIN_C5        // optional: monitors time in sci rx and tx isrs
#define         SCI_TX              PIN_C6          // SCI: TX line (PC_IN)
#define         SCI_RX              PIN_C7          // SCI: RX line (PC_OUT)

// --- use the i2c hardware (FAST=400KHz, SLOW=100KHz):
//
#use I2C(MASTER, SDA=I2C_SDA, SCL=I2C_SCL, FAST, RESTART_WDT, FORCE_HW)

// --- use the sci-usart hardware:
//
#use RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, BITS=8, PARITY=N, ERRORS)
//
//  Note: If ENABLE is used (for 485 chip), then putc will wait for entire duration of
//        byte output in the TBE_isr -- removing ENABLE allows fast tranfer to tx register.
//#use RS232 (BAUD=38400, XMIT=SCI_TX, RCV=SCI_RX, ENABLE=PIN_Cx, BITS=8, PARITY=N, ERRORS)

#use STANDARD_IO (C)


// ----- misc:
#priority     TIMER1, TIMER2, RDA, TBE              // give timer1 highest priority


// ----- turn ON to enable debug output (conditional-compile with printf output):
//        note: printf can increase needed rom size quite a bit
#define       DEBUG_TC_PARSE              OFF       // ON to turn on TC parsing debugging
#define       DEBUG_TC_EXE                OFF       // ON to turn on TC cmd execution debugging
#define       DEBUG_CONNECTION            OFF       // ON to turn on connection debugging
#define       DEBUG_EXT_EEPROM1           OFF       // ON to turn on ext-eeprom write/read general debugging
#define       DEBUG_EXT_EEPROM1A          OFF       // ON to turn on ext-eeprom write-ack/retry debugging
#define       DEBUG_EXT_EEPROM2           OFF       // ON to turn on ext-eeprom test general debugging
#define       DEBUG_EXT_EEPROM2A          OFF       // ON to turn on ext-eeprom test failure-mssg debugging
#define       DEBUG_EXT_EEPROM2B          OFF       // ON to turn on ext-eeprom test address debugging


// ------ error codes for normal error mesages:
#define       ERR_WD_RESET                0         // watchdog-reset error code
#define       ERR_CMD_TYPE                1         // command-type error code (not TW or TR)
#define       ERR_CMD_ID                  2         // command-id error code
#define       ERR_CMD_NUMPARAM_CS         3         // num-params or checksum error code
#define       ERR_CMD_PORTSPEED_PARAM     4         // ports or speeds cannot have same value
#define       ERR_CMD_PARAM               5         // invalid command param


// ------ internal diagnostic-error codes for loading into global diag_code:
#define       DIAG_NONE                   0         // (no err must be 0)
#define       DIAG_SCI_INBUF_FULL         1         // SCI (232) input buffer overflow
#define       DIAG_PA_OUTBUF_FULL         2         // PA output buffer overflow
#define       DIAG_PA_INBUF_FULL          3         // PA input buffer overflow
#define       DIAG_PB_OUTBUF_FULL         4         // PB output buffer overflow
#define       DIAG_PB_INBUF_FULL          5         // PB input buffer overflow
#define       DIAG_MSSG_MODE_PARAM        6         // invalid mode param in send_tc_connect_mode_mssg()
#define       DIAG_CONNECT_MODE_PARAM     7         // invalid mode param in connect(mod)


// ----- define internal eeprom locations (1024 total in 18F2620 -- 0x0000 TO 0x03FF):
#define       INT_EE_STRING_1_STR         0x00      // (to 0x07) general-use string
#define       INT_EE_STRING_2_STR         0x08      // (to 0x0F) general-use string
#define       INT_EE_STRING_3_STR         0x10      // (to 0x17) general-use string
#define       INT_EE_STRING_4_STR         0x18      // (to 0x1F) general-use string
#define       INT_EE_STRING_5_STR         0x20      // (to 0x27) general-use string
#define       INT_EE_STRING_6_STR         0x28      // (to 0x2F) general-use string
#define       INT_EE_STRING_7_STR         0x30      // (to 0x37) general-use string
#define       INT_EE_STRING_8_STR         0x38      // (to 0x3F) general-use string
#define       INT_EE_STRING_9_STR         0x40      // (to 0x47) general-use string
#define       INT_EE_STRING_10_STR        0x48      // (to 0x4F) general-use string
#define       INT_EE_STRING_11_STR        0x50      // (to 0x57) general-use string
#define       INT_EE_STRING_12_STR        0x58      // (to 0x5F) general-use string
#define       INT_EE_STRING_13_STR        0x60      // (to 0x67) general-use string
#define       INT_EE_STRING_14_STR        0x68      // (to 0x6F) general-use string
#define       INT_EE_STRING_15_STR        0x70      // (to 0x77) general-use string
#define       INT_EE_STRING_16_STR        0x78      // (to 0x7F) general-use string

#define       INT_EE_FIRMWARE_VER_H       0x80      // firmware version high byte
#define       INT_EE_FIRMWARE_VER_L       0x81      // firmware version low byte

//            (unused)                   (0x82 - 0x8F)

#define       INT_EE_MODE                 0x90      // connect config
#define       INT_EE_PORT_A_PORT          0x91      // connect config
#define       INT_EE_PORT_A_BITS          0x92      // connect config
#define       INT_EE_PORT_A_RATE          0x93      // connect config
#define       INT_EE_PORT_B_PORT          0x94      // connect config
#define       INT_EE_PORT_B_BITS          0x95      // connect config
#define       INT_EE_PORT_B_RATE          0x96      // connect config

//            (unused)                   (0x97 - 0x9F)

#define       INT_EE_MAX_CHARS_PER_LINE   0xA0      // rx-options
#define       INT_EE_MOTOR_POWER_MODE     0xA1      // rx-options
#define       INT_EE_PROG_FLAGS_1         0xA2      // rx-options
#define       INT_EE_PROG_FLAGS_2         0xA3      // rx-options

//            (unused)                   (0xA4 - 0xAF)

#define       INT_EE_AUTO_CRLF_STR        0xB0      // (to 0xB7) rx-options string
#define       INT_EE_START_OF_MSSG_STR    0xB8      // (to 0xBF) rx-options string
#define       INT_EE_END_OF_MSSG_STR      0xC0      // (to 0xC7) rx-options string
#define       INT_EE_COND_CODE_STR        0xC8      // (to 0xCF) rx-options string
#define       INT_EE_SELCAL_STATION_STR   0xD0      // (to 0xD7) rx-options string
#define       INT_EE_SELCAL_ALL_STR       0xD8      // (to 0xDF) rx-options string

//            (unused)                   (0xD9 - 0xDF)
//            (unused)                   (0xE0 - 0xEF)
//            (unused)                   (0xF0 - 0xFF)


// ----- motor_power_mode cases:
#define       MOT_PWR_OFF                 0         // motor power mode is off
#define       MOT_PWR_AUTOSTART_CHAR      1         // motor power mode is autostart sense any char (/timeout?)
#define       MOT_PWR_AUTOSTART_STRING    2         // motor power mode is autostart sense start/end-of-mssg strings
#define       MOT_PWR_AUTOSTART_SPECIAL   3         // motor power mode is autostart special (transistions?)
#define       MOT_PWR_SELCAL_STATION      4         // motor power mode is selcal-station sense string
#define       MOT_PWR_SELCAL_ALL          5         // motor power mode is selcal-all sense string
#define       MOT_PWR_SELCAL_BOTH         6         // motor power mode is selcal-station-and-all


// ----- baudot/ustty stuff:
#define       BLANK                       0x00
#define       BLF                         0x02
#define       BSP                         0x04
#define       BELL                        0x05
#define       BCR                         0x08
#define       FIGS                        0x1B
#define       LTRS                        0x1F
#define       LTRS_FIGS_UNKNOWN           0xFF


// ----- format of chars used in rx_options:
#define       BAUDOT                      0
#define       ASCII                       1



// ----- header files:
#include      "vaux_std.h"                         // standard stuff


// ----- global variables, misc (if static, compiler inits to 0 unless init here):
int8           diag_code = DIAG_NONE;              // diagnostic-error codes for main prog to find

int8           demo_mssg_num;                      // demo-message number (index to ext eeprom mssg)
int16          demo_mssg_char_addr;                // points to next addr in ext eeprom for write/read
int16          demo_mssg_chars_remaining;          // init to max demo-mssg size then count down
int1           demo_mssg_loading = 0;              // flag for serial input routine


// ----- globals (these get initialized before calling connect() routine):
int8           port_A_bits;                        // connection param
int8           port_A_port;                        // connection param
int8           port_A_rate;                        // connection param
int8           port_B_bits;                        // connection param
int8           port_B_port;                        // connection param
int8           port_B_rate;                        // connection param


// ----- globals (these get initialized or updated in connect() routine):
int8          mode;                                 // connection param
int8          TTY_port_image;                       // image of tty-loop/tu port
int8          MOT_port_image;                       // image of port driving motor and ptt signals
int8          baudot_in_current_case;               // table offset: 0 for LTRS, 32 for FIGS
int8          baudot_out_current_case;              // actual case:  0x1F for LTRS, 0x1B for FIGS
int8          baudot_out_new_case;                  // 0 for no tx case change, else actual case
int8          tx_cr_char_count;                     // tx-options: num-chars since last cr
int8          rx_cr_char_count;                     // rx-options: num-chars since last cr
int8          start_of_mssg_matches;                // rx-options: num-chars matched in start_of_mssg_str
int8          end_of_mssg_matches;                  // rx-options: num-chars matched in end_of_mssg_str
int8          cond_code_matches;                    // rx-options: num-chars matched in cond_code_str
int8          selcal_station_matches;               // rx-options: num-chars matched in selcal_station_str
int8          selcal_all_matches;                   // rx-options: num-chars matched in selcal_all_str
int8          flags_1;                              // rx-options: flags
#bit            got_start_of_mssg_match   = flags_1.0
#bit            got_end_of_mssg_match     = flags_1.1
#bit            got_cond_code_match       = flags_1.2
#bit            got_selcal_station_match  = flags_1.3
#bit            got_selcal_all_match      = flags_1.4
////////////#bit            got_wru_match             = flags_1.5


// ----- globals for clock routine:
int8          second = 0;                           // current second
int8          minute = 23;                          // current minute
int8          hour = 16;                            // current hour
int8          day = 1;                              // current day
int8          month = 11;                           // current month
int8          year_h = 19;                          // current year (high digits)
int8          year_l = 58;                          // current year (low digits)
int8          clock_mode = 0;                       // off, print-every-hour...
int8          clock_loop = 0;                       // test (FP-display only), or loop for print

int1          pm_flg;

int8          print_hour;



int8          clock_flags = 0x03;                   // options for clock
#bit            ck_12_hour_time_format      = clock_flags.0
#bit            ck_standard_date_format     = clock_flags.1
//#bit            xxx                       = clock_flags.2
//#bit            xxx                       = clock_flags.3
//#bit            xxx                       = clock_flags.4
//#bit            xxx                       = clock_flags.5
//#bit            xxx                       = clock_flags.6
//#bit            xxx                       = clock_flags.7


// ----- globals, eeprom programmable:
//          if new global added, it must be handled in other places:
//            - define eeprom location (above)
//            - read_prog_params_from_eeprom()
//            - write_prog_params_to_eeprom()
//            - restore_factory_settings()
//            - TW/TR commands/mssgs
//            - where used in prog

int8          auto_crlf_str[8];

#if (STREAM_OPTIONS)
int8          start_of_mssg_str[8];
int8          end_of_mssg_str[8];
int8          cond_code_str[8];
int8          selcal_station_str[8];
int8          selcal_all_str[8];
#endif

int8          max_chars_per_line;                 // max num chars per line before auto-crlf
//static int8   auto_crlf_nul_pads;                 // num nulls added after auto-crlf in ascii mode
int8          motor_power_mode;                   // off, autostart, and selcal options

// ----- general flags enable features (0 = off, 1 = on):
int8          prog_flags_1;
#bit          unconn_loops_mark     = prog_flags_1.0
#bit          tu_data_inversion     = prog_flags_1.1

// ----- baudot-specific flags enable features (0 = off, 1 = on):
int8          prog_flags_2;
#bit          rx_unshift_on_space   = prog_flags_2.0
#bit          rx_unshift_on_cr      = prog_flags_2.1
#bit          rx_auto_crlf_insert   = prog_flags_2.2
#bit          rx_diddle_filter      = prog_flags_2.3
#bit          tx_diddle_insertion   = prog_flags_2.4
#bit          si_so_for_ltrs_figs   = prog_flags_2.5
#bit          wru_reply             = prog_flags_2.6



// ----- prototypes from tc_subs.c, so funcs are 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


// ----- interrupt routine for sci (usart) i/o (change default buffer sizes first, if desired):
#define       SCI_RXBUF_SIZE        500           // define before sci_16bit_isr.c to change
#define       SCI_RXBUF_HIGH        450           // "
#define       SCI_RXBUF_LOW          20           // "
#define       SCI_TXBUF_SIZE        300           // "
#define       SCI_TXBUF_HIGH        260           // "

#include      "tc_sci_isr.c"           // interrupt routines for sci (usart) i/o
                                       //  (8-bit isr cannot have #device *=16 above)

// ----- project-specific c files:
//        Note:   Order is important, since compiler does just one pass to resolve symbols
//                (ie: later includes can see previous includes, not not vice-versa).
//                Keep prototypes/defines/globals local to each file if possible.
//                If a prototype/define/global cannot be seen by the simple ordering below,
//                then it is placed above here.

#include      "vaux_std.c"            // routines for misc handy stuff
#include      "eeprom2.c"             // routines for external eeprom
#include      "tc_port_io.c"          // routines for tty port send/receive, and software uarts (& timer-1 intr)
#include      "tc_clock.c"            // routines for time and date counting, and seconds-time-out for motor control (& timer-2 intr)
#include      "tc_mssgs.c"            // routines for TTY-Connect messages
#include      "tc_cmds.c"             // routines for TTY-Connect commands, and PC232 port rx parsing
#include      "tc_subs.c"             // routines for translating baudot to/from ascii-67...


//--------------------------------------------------------------------------------------------
//  Main Entry (boot/reset):

void main()
{
  // (vars defined here are not global -- only seen by main)

  int8 i, c, pc, sys_start;


  // ----- store sys_start for use later:
  //          (if we do this much later in this routine, it gets set to normal, even if actually wdt)
  sys_start = restart_cause();                      // save, in case it was watchdog reset


  // ----- init port A -- pins are input by default:
  setup_adc_ports (NO_ANALOGS);                     // no analog pins
  set_tris_A(0x00);                                 // init directions (1=input=default, 0=output)
  PORT_A = ALL_MOTORS_OFF;                          // init output pins (all motors off; ptt off)


  // ----- init port B -- pins are input by default:
  set_tris_B(0x55);                                 // init directions (1=input=default, 0=output)
  PORT_B = ALL_LOOPS_MARK_TU_MARK;                  // init output pins (tty out ports all to mark)


  // ----- init port C -- pins are input by default:
  set_tris_C(0x99);                                 // init directions (1=input=default, 0=output)
  PORT_C = 0x60;                                    // init output pins


  // ----- timer-1 interrupt:
  setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
  set_timer1 (T1RELOAD);                            // T1RELOAD defined in tc_port_io.c
  enable_interrupts (INT_TIMER1);


  // ----- timer-2 interrupt:
  setup_timer_2 (T2_DIV_BY_16, T2PERIOD, T2POSTSCALE);  // T2PERIOD and T2POSTSCALE defined in tc_clock.c
  set_timer2 (0);
  enable_interrupts (INT_TIMER2);


  // ----- watchdog timer:
  setup_wdt (WDT_ON);


  // ----- setup sci (usart) receive interrupt (in sci_isr.c):
  enable_interrupts (INT_RDA);


  // ----- enable interrupt system:
  enable_interrupts (GLOBAL);


  // ----- if CONTROL pushbutton is held/released at power-up, restore factory settings:
  if (control_pb_pressed())
  {
    printf(send_FP_tx_byte, "\nRestore-FactSet");
    restore_factory_settings();                     // will send mssg also
  }


  // ----- if firmware version stored in eeprom is not this version, restore factory settings:
  if (read_eeprom(INT_EE_FIRMWARE_VER_H) != FIRMWARE_VERSION_HI)
    restore_factory_settings();                   // will send mssg also
  if (read_eeprom(INT_EE_FIRMWARE_VER_L) != FIRMWARE_VERSION_LO)
    restore_factory_settings();                   // will send mssg also


  // ----- get programmable params and connection info from eeprom, and load globals:
  read_prog_params_from_eeprom();                   // reads programmable params
  read_conn_params_from_eeprom();                   // reads connection params


  // ----- init variables:
  use_XONXOFF_flg = ON;                             // sci_int: ON to enable SW handshaking
  echo_chars_flg = OFF;                             // option: ON to echo 232 rx chars for testing
  use_leading_zeroes_flg = OFF;                     // option: ON for fixed 3-char mssg params


  // ----- send boot mssg to front-panel:
  send_FP_tx_byte("\nTTY-Connect");


  // ----- send boot message to serial port, and flash Status led:
  putc(XON);                                        // just in case control system is hung

  send_sci_tx_byte("\r\n\r\nTTY-Connect Ver: ");    // this is only non-protocol mssg
  output_digit(FIRMWARE_VERSION_HI);
  send_sci_tx_byte('.');
  output_digit(FIRMWARE_VERSION_LO);
  send_sci_tx_byte('\r');
  send_sci_tx_byte('\n');

  send_tc_sysinfo_mssg();                           // this also flashes Status led


  // ----- check to see if this boot was a watchdog reset:
  if(sys_start == WDT_TIMEOUT)                      // can also be NORMAL_POWER_UP,
  {                                                 //    MCLR_FROM_SLEEP, or WDT_FROM_SLEEP
    // diag message on serial port, and turn on status led (if installed):
    send_sci_tx_byte("\n\rWDT reset!\n\r\n\r");
    output_high (STATUS_LED);
  }


  // ----- initialize connection:
  connect(mode);                                    // initiates connection with previous mode (from eeprom)
  send_tc_connect_mode_mssg();                      // send connection mssg to PC232



//--------------------------------------------------------------------------------------------
    // ========== init mssg determined by connection mode:
    switch(mode)
    {

    // -------- connect mode-1:  Ascii PC to Baudot TTY/TU (on port B):
    case 1:
    // -------- connect mode-4:  Baudot TU (on port A) to Baudot TTY (on port B):
    case 4:
    // -------- connect mode-6:  Baudot ports w/ speed change:
    case 6:

      // send baudot connection mssg to TTY-port-B (LTRS CR LF T C CR LF LF LF LF LF LF)
      send_PB_out_char("\x1F\x08\x02\x10\x0E\x08\x02\x02\x02\x02\x02\x02");
      break;

    // -------- connect mode-2:  Ascii PC to 110-baud Ascii TTY/TU (on port B):
    case 2:
    // -------- connect mode-5:  Baudot TU (on port A) to 110-baud Ascii TTY (on port B):
    case 5:
      // send ascii connection mssg to TTY-port-B  (CR LF T C CR LF LF LF LF LF LF)
      send_PB_out_char("\r\nTC\r\n\n\n\n\n\n");
      break;

    // (will get here for modes 3, 10, and 11)
    default:
      break;
    }


//--------------------------------------------------------------------------------------------
//  Main Event Loop:

  while(1)
  {

    // ========== data steering determined by connection mode (port params set when connected):
    switch(mode)
    {

    // -------- connect mode-1:  Ascii PC to Baudot TTY/TU (on port B):
    case 1:

      // --- output to Port B:
      if (PB_out_buf_count == 0)              // wait until PB buffer empties before adding any more chars
      {
        // get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
        //    and parse/execute incoming TTY-Connect command, if complete.
        //    Char pc is returned to pass to tty port as desired (if non-zero).
        pc = get_sci_rx_byte_and_process_tc_cmd();
        if (pc)                               // if PC232 char avail, send to port B
        {
          // --- port B is baudot, so convert ASCII-67 to USTTY (baudot):
          c = ascii_to_ustty (pc);                    // convert char
          if (baudot_out_new_case)                    // if baudot_out_new_case is non-zero,
            rx_options_and_send_PB_out_char(baudot_out_new_case, BAUDOT);    // send change case (LTRS/FIGS) to port B
          rx_options_and_send_PB_out_char(c, BAUDOT); // baudot output char piped through rx stream options
        }
      }

      // --- input from Port B:
      c = get_PB_in_char();                   // get port B char, if avail, and send to PC232 port
      if (c)
      {
        // port B is baudot, so convert USTTY (baudot) to ASCII-67
        c = ustty_to_ascii (c);               // convert char
        send_sci_tx_byte(c);                  // send char to PC232 port
      }

      break;


    // -------- connect mode-2:  Ascii PC to 110-baud Ascii TTY/TU (on port B):
    case 2:
    // -------- connect mode-3:  Raw PC to Raw TTY/TU (on port B):
    case 3:

      // --- output to Port B:
      if (PB_out_buf_count == 0)              // wait until PB buffer empties before adding any more chars
      {
        // get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
        //    and parse/execute incoming TTY-Connect command, if complete.
        //    Char pc is returned to pass to tty port as desired (if non-zero).
        pc = get_sci_rx_byte_and_process_tc_cmd();
        if (pc)                               // if PC232 char avail, send to port B
        {
          if (pc == CRLF)                     // if special CRLF char,
          {
            send_PB_out_char(CR);             // send CR to port B
            pc = LF;                          // then send LF to port B
          }
          if(mode == 2)
            rx_options_and_send_PB_out_char(pc, ASCII); // ascii output char piped through rx stream options
          else
            send_PB_out_char(pc);             // send char to port B
        }
      }

      // --- input from Port B:
      c = get_PB_in_char();                   // get port B char, if avail, and send to PC232 port
      if (c)
        send_sci_tx_byte(c);

      break;


    // -------- connect mode-4:  Baudot TU (on port A) to Baudot TTY (on port B):
    case 4:

      // --- get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
      //    and parse/execute incoming TTY-Connect command, if complete.
      //    Char pc is returned to pass elsewhere as desired (if non-zero).
      pc = get_sci_rx_byte_and_process_tc_cmd();

      // --- output to Port B:
//    if (PB_out_buf_count == 0)              // wait until PB buffer empties before adding any more chars
//    {
        c = get_PA_in_char();                 // get TU port char, if avail, and send to port B
        if (c)
          rx_options_and_send_PB_out_char(c, BAUDOT); // baudot output char piped through rx stream options
//    }

      // --- input from Port B:
      c = get_PB_in_char();                   // get port B char, if avail, and send to TU port
      ///tx_options
      if (c)
        send_PA_out_char(c);

      break;


    // -------- connect mode-5:  Baudot TU (on port A) to 110-baud Ascii TTY (on port B):
    case 5:

      // --- get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
      //    and parse/execute incoming TTY-Connect command, if complete.
      //    Char pc is returned to pass elsewhere as desired (if non-zero).
      pc = get_sci_rx_byte_and_process_tc_cmd();

      // --- output to Port B:
//    if (PB_out_buf_count == 0)              // wait until PB buffer empties before adding any more chars
//    {
        c = get_PA_in_char();                 // get TU port char, if avail, and send to port B

        if (c)
        {
          // port A is baudot TU, so convert USTTY (baudot) to ASCII-67
          c = ustty_to_ascii (c);             // convert char
          rx_options_and_send_PB_out_char(c, ASCII); // ascii output char piped through rx stream options
        }
//    }

      // --- input from Port B:
      c = get_PB_in_char();                   // get port B char, if avail, and send to TU port
      if (c)
      {
        // --- port B is baudot TU, so 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);    //   send change case (LTRS/FIGS) to port A
        send_PA_out_char(c);                        // send char
      ///tx_options
      }

      break;


    // -------- connect mode-6:  Baudot ports w/ speed change:
    case 6:

      // --- Port A faster than Port B:
      if(port_A_rate > port_B_rate)
      {
        // --- output to Port B (with buffering via PC232):
        c = get_PA_in_char();                   // get port A char, if avail, and send to PC232 port
        if (c)
          send_sci_tx_byte(c);

        if (PB_out_buf_count == 0)              // wait until PB buffer empties before adding any more chars
        {
          // --- get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
          //      and parse/execute incoming TTY-Connect command, if complete.
          //      Char pc is returned to pass to tty port as desired (if non-zero).
          pc = get_sci_rx_byte_and_process_tc_cmd();
          if (pc)                               // if PC232 char avail, send to port B
          rx_options_and_send_PB_out_char(pc, BAUDOT); // baudot output char piped through rx stream options
        }

        // --- input from Port B:
        c = get_PB_in_char();                   // get port B char, if avail, and send to Port A
        if (c)
          send_PA_out_char(c);
      }

      // --- Port B faster than Port A:
      else
      {
        // --- output to Port A (with buffering via PC232):
        c = get_PB_in_char();                   // get port B char, if avail, and send to PC232 port
        if (c)
          send_sci_tx_byte(c);

        if (PA_out_buf_count == 0)              // wait until PA buffer empties before adding any more chars
        {
          // --- get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
          //      and parse/execute incoming TTY-Connect command, if complete.
          //      Char pc is returned to pass to tty port as desired (if non-zero).
          pc = get_sci_rx_byte_and_process_tc_cmd();
          if (pc)                               // if PC232 char avail, send to port A
            send_PA_out_char(pc);
        }

        // --- input from Port A:
        c = get_PA_in_char();                   // get port A char, if avail, and send to Port B
        if (c)
          rx_options_and_send_PB_out_char(c, BAUDOT); // baudot output char piped through rx stream options
      }

      break;


    // (will get here for modes 10 and 11)
    default:
      // get next PC232 port char, if avail (rx stream is throttled with xon/xoff),
      //    and parse/execute incoming TTY-Connect command, if complete.
      //    Char pc is returned to pass to tty port as desired (if non-zero).
      pc = get_sci_rx_byte_and_process_tc_cmd();
      break;
    }



    // ========== every ...hour/minute we will update clock info:
  
    switch(clock_mode)
    {
      case 0:                                                                   // clock_mode 0: clock off
         ck_print_flg = 0;
         break;
    
      case 1:                                                                   // clock_mode 1: print clock every-hour
         if(t2_hour_flag)
         {
            ck_print_flg = 1;
            t2_hour_flag = 0;
         }
         break;
    
      case 2:                                                                   // clock_mode 2: print clock every-15-min
         if(t2_15min_flag)
         {
            ck_print_flg = 1;
            t2_15min_flag = 0;
         }
         break;
    
      case 3:                                                                   // clock_mode 3: print clock every-1-min
         if(t2_minute_flag)
         {
            ck_print_flg = 1;
            t2_minute_flag = 0;
         }
         break;
    
    }
    
    if(ck_print_flg)
    {
      ck_print_flg = 0;

      // --- 12-hour mode -- suppress leading zero on hour:
      if(ck_12_hour_time_format)
      {
        print_hour = hour;       // hour stays in 24-hr format internally

        if(hour >= 12)
          pm_flg = 1;
        else
          pm_flg = 0;

        if(hour > 12)
          print_hour -= 12;

        if(hour == 0)
          print_hour = 12;

        printf(send_FP_tx_byte, "\n%2u:%02u:%02u", print_hour, minute, second);

        if(pm_flg)
          send_FP_tx_byte(" pm");
        else
          send_FP_tx_byte(" am");

      }

      // --- 24-hour mode -- print leading zero on hour:
      else
      {
        printf(send_FP_tx_byte, "\n%02u:%02u:%02u", hour, minute, second);
      }

////ck_standard_date_format

    }



    // ========== Check CONTROL pushbutton:

    if (control_pb_pressed())
    {

      printf(send_FP_tx_byte, "\ntest: %u", test_ext_eeprom(ext_eeprom_max_addr));
      //printf(send_FP_tx_byte, "\ncontrol-pb");
    }



    // ========== If diagnostic-error code loaded somewhere, send message:

    if (diag_code)
    {
      send_tc_diag_mssg(diag_code);
      diag_code = DIAG_NONE;
    }


    // ========== keep watchdog happy:

    restart_wdt();

  }

}


//--------------------------------------------------------------------------------------------
//  reserve space for tiny bootloader  (www.etc.ugal.ro/cchiculita/software/picbootloader.htm)
//
//    18F2620 tiny-bootloader needs 100-words (200 bytes) at top of 32K-words.
//           >>> this chip needs to org as bytes (allocate top 256-bytes out of 64K-bytes):
#org 0xFF00, 0xFFFF {}

