/*
	CSREMOTE.C
	
   - Works with any Z-World Board that has the Z180 serial port 1 
     configurable as a half duplex RS485.
     
   - This is the slave program to network to the master running the 
 	  program CZ0REM.C.

	Commands typed from a dumb terminal pass thru the master to the slave
 	if the slave number matches the value typed.
 	  
	For example, >2 t would get the time from slave no. 2.

	The following commands are implemented for the slave:
	
      >x t   -  get the time and date from slave x-0x30
      >x h   -  get a small help file for slave x-0x30
      >x s v y xxx.xx   - write xxx.xx to variable with tag y
      >x s t hh:mm:ss   - write time hh:mm:ss to slave
      >x s d mm-dd-yy-w - write date mm-dd-yy-w to slave
      >x u XXXXX        - upload file to slave x-0x30 starting at location
                          0xXXXXX.
                        - the master actually serve the slave with command
                          'r'
      >x m     - request a menu from slave
      >x x XXXXX   - read 16 bytes starting from memory location 0xXXXXX.
      >x w YY XXXXX  - write byte 0xYY to location 0xXXXXX of slave x-0x30.


       Hardware connection:
        master                       slave
        TX+  |  -----------------  |  TX+
        RX+  |                     |  RX+
        TX-  |  -----------------  |  TX-
        RX-  |                     |  RX-

        Master slave communication uses the new opto22 9th bit binary
       protocol. This means that only the targeted slave will open its
       communication to receive the information other than the slave
       address broadcast during the 9th bit mode.

	Need the following libraries: modem232.lib, network.lib and drivers.lib.
*/

#use "default.h"

#if (BOARD_TYPE==CPLC_BOARD) || (BOARD_TYPE==L_STAR))
#use cplc.lib
#endif

// uncomment the following line if you are using a pk23xx controller
//#use eziopk23.lib

#define SLAVE_NO 2

char op_rbuf[255];           // receiver buffer
char op_tbuf[255];           // transmitter buffer

struct s_array
{
    char name[20];
    char tag;
    float value;
};

struct s_array vdata[4]; /* define as global so as not to keep passing back
                            and forth                                    */
int           no_of_pages;
unsigned int  next_bbr;
unsigned long nxtBaseAddress;

// function prototypes
void reset_tx_buf(char *buf);
void write_tx(char *dest, char *source);
void process_buf1(char *buf);
void set_data_or_time(char *buf);
void store_data(char *buf);
void help_menu(void);
void variables_menu(void);
void main_menu(void);
struct s_array makedata(char name[], char tag, float value);
void send_prompt(char *buf);
void snd_time_date(void);
void set_the_time(char *buf);
void set_the_date(char *buf);
void decimal(float value, char *buf, int ndecimal);
void get_mem(char *buf);
void write_mem(char *buf);
unsigned long a_to_lh(char *buf);
void remote_download(char *buf);
void remote_data(char *buf);
int  check_crc(unsigned long baseAddress);

main()
{
    int          i;
    unsigned int testcrc;

    no_of_pages = 0;

#if ((BOARD_TYPE==CPLC_BOARD) + (BOARD_TYPE==L_STAR))
    uplc_init();
    lcd_erase();
    lcd_printf(0x01000000L, "Hello World");
#endif    
    
    op_kill_z1();
    
    // Initialize dummy data for demo
    vdata[0] = makedata("(a) data1       ", 'a', 267.99);
    vdata[1] = makedata("(b) data2       ", 'b', 23.00);
    vdata[2] = makedata("(c) data3       ", 'c', 534.123);
    vdata[3] = makedata("(d) data4       ", 'd', 6456.32);
    op_init_z1(19200 / 1200, op_rbuf, SLAVE_NO);
    while (1)
    {
    		hitwd();
        if (check_opto_command() == 1)
        {
            process_buf1(op_rbuf + 2);
        }
        runwatch();
    }
}
/**************************
void reset_tx_buf()
   - reset any transmit buffer
***************************/
void reset_tx_buf(char *buf)
{
    buf[0] = '\0';           // end of string
}

/******************************************************************
void write_tx(char *dest, char *source)
     - write response to the transmit buffer tbuf.
     - note that we have been assuming all reply to be in asci.
     - reply can also be in binary, but write_tx would have to handle
       the building of the reply differently.
******************************************************************/
void write_tx(char *dest, char *source)
{
    strcat(dest, source);
}

/*****************************************************************
void process_buf1()
    - this is a generic processing of information in rbuf.
    - the slave would receive a string using the following protocol:
       rbuf[0] - slave address in binary
       rbuf[1] - number of character to follow after this byte
       rbuf[2] - command or whatever
       rbuf[3] - command or whatever
       .
       .
       .
       rbuf[n-1] - high byte crc check
       rbuf[n]   - low byte crc check
    - this sample program mostly uses asci transaction for reply to the
      dumb terminal, but the opto22 protocol could just as well handle
      binary protocol.
*********************************************************************/
void process_buf1(char *buf)
{
    char c;
    int  i;

    reset_tx_buf(op_tbuf);
    switch (*buf)
    {
        case 'v':
            variables_menu();
            break;
        case 't':
            snd_time_date();
            break;
        case 's':
            set_data_or_time(buf + 2);
            break;
        case 'm':
            main_menu();
            break;
        case 'h':
            help_menu();
            break;
        case 'x':
            get_mem(buf + 2);
            break;
        case 'w':
            write_mem(buf + 2);
            break;
        case 'r':
            remote_download(buf + 1);
            break;
        case 'd':
            remote_data(buf + 1);
            break;
        case 'z':
            if (check_crc(nxtBaseAddress) == 1)
            {
                write_tx(op_tbuf, "slave crc okay");
                send_prompt(op_tbuf);
                replyOpto22(op_tbuf, strlen(op_tbuf), 0);
                for (i = 0; i < 32000; i++)
                    ;
                op_kill_z1();
                resetZ180Int(); /* turn off timer interrupts
                                   serial interrupts
                                   INTs' interrupts
                                   DMA interrupts
                                   SIO's,CTC2,CTC3 for LG
                                   PIO's for LG,TG, LPLC     */
                newbbr(0x2200, next_bbr);
            }
            else
            {
                write_tx(op_tbuf, "slave has bad crc");
                send_prompt(op_tbuf);
                replyOpto22(op_tbuf, strlen(op_tbuf), 0);
            }
            break;
        default:
            write_tx(op_tbuf, "got nothing");
            send_prompt(op_tbuf);
            replyOpto22(op_tbuf, strlen(op_tbuf), 0);
            break;
    }
}


/*********************************
void set_data_or_time(char *buf)
    Set the data, time or date.
**********************************/
void set_data_or_time(char *buf)
{
    switch ( *(buf) )
    {
        case 'v':
            store_data(buf + 2);
            break;
        case 't':
            set_the_time(buf + 2);
            break;
        case 'd':
            set_the_date(buf + 2);
            break;
        default:
            break;
    }
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);

}


/***********************************
void store_data(char *buf)
***********************************/
void store_data(char *buf)
{
    int i, j;

    for (i = 0; i < 4; i++)
    {
        if (vdata[i].tag == * (buf))
        {
            j = 0;
            vdata[i].value = atof(buf + 2);
            return;
        }
    }
    write_tx(op_tbuf, "No matching tag");
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);

}

/****************************************
void help_menu()
   - Display the help menu
*****************************************/
void help_menu(void)
{
    write_tx(op_tbuf, "set variables:  >'address' s v 'tag' value");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "set time: >'address' s t xx:yy:zz         ");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "set date: >'address' s d mm-dd-yyyy-d     ");
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}

/******************************************************
void variables_menu()
   - display the values of the different variables
*****************************************************/
void variables_menu(void)
{
    int  i;
    char s_pad[20];

    write_tx(op_tbuf, "tag name value");
    send_prompt(op_tbuf);
    for (i = 0; i < 4; i++)
    {
        write_tx(op_tbuf, vdata[i].name);
        decimal(vdata[i].value, s_pad, 4);
        write_tx(op_tbuf, s_pad);
        send_prompt(op_tbuf);
    }
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);

}

/*********************************************
void main_menu()
   Display the main menu
***********************************************/
void main_menu(void)
{
    write_tx(op_tbuf, "Main Menu");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "(m) main menu");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "(v) variables");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "(t) time/date");
    send_prompt(op_tbuf);
    write_tx(op_tbuf, "(h) help     ");
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}

/**************************************
makedata()
   - example to initialize data each structure array
***************************************/
struct s_array makedata(char name[], char tag, float value)
{
    struct s_array temp;
    strcpy(temp.name, name);
    temp.tag   = tag;
    temp.value = value;
    return temp;
}

/*******************************************
void send_prompt(char *buf)
      Put prompt string into the buffer *buf
*******************************************/
void send_prompt(char *buf)
{
    write_tx(buf, "\r\n>");
}

char *days[] =
     {
         "sun ", "mon ", "tues", "wed ", "thur", "fri ", "sat ", "??? "
     };

/**************************************************
void snd_time_date()
****************************************************/
void snd_time_date()
{
    struct tm tim;
    char s_pad[25];

    tm_rd(&tim);
    strcpy(s_pad, days[tim.tm_wday]);
    s_pad[4] = ' ';
    s_pad[5] = tim.tm_mon / 10 + 0x30;
    s_pad[6] = tim.tm_mon - (s_pad[5] -0x30) * 10 + 0x30;
    s_pad[7] = '-';
    s_pad[8] = tim.tm_mday / 10 + 0x30;
    s_pad[9] = tim.tm_mday - (s_pad[8] -0x30) * 10 + 0x30;
    s_pad[10] = '-';
    if(tim.tm_year >= 80)
    {
      s_pad[11] = '1';
      s_pad[12] = '9';
    }
    else
    {
      s_pad[11] = '2';
      s_pad[12] = '0';
    }
    s_pad[13] = tim.tm_year / 10 + 0x30;
    s_pad[14] = tim.tm_year - (s_pad[13] -0x30) * 10 + 0x30;
    s_pad[15] = ' ';
    s_pad[16] = tim.tm_hour / 10 + 0x30;
    s_pad[17] = tim.tm_hour - (s_pad[16] -0x30) * 10 + 0x30;
    s_pad[18] = ':';
    s_pad[19] = tim.tm_min / 10 + 0x30;
    s_pad[20] = tim.tm_min - (s_pad[19] -0x30) * 10 + 0x30;
    s_pad[21] = ':';
    s_pad[22] = tim.tm_sec / 10 + 0x30;
    s_pad[23] = tim.tm_sec - (s_pad[22] -0x30) * 10 + 0x30;
    s_pad[24] = '\0';

    write_tx(op_tbuf, s_pad);
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);

}

/******************************************************************
void set_the_time(char *stemp)
   Set the time of the real time clock based on the character string.
   string format >>  hh:mm:ss
              e.g.   10:11:12
********************************************************************/
void set_the_time(char *stemp)
{
    struct tm tim;
    tm_rd(&tim);
    tim.tm_sec  = (stemp[6] -0x30) * 10 + (stemp[7] -0x30);
    tim.tm_min  = (stemp[3] -0x30) * 10 + (stemp[4] -0x30);
    tim.tm_hour = (stemp[0] -0x30) * 10 + (stemp[1] -0x30);
    tm_wr(&tim);
}

/*******************************************************************
void set_the_date(char *stemp)
   Set the date of the real time clock based on the character string.
   string format >>   mm-dd-yyyy-w
                e.g.  11-11-1993-4
********************************************************************/
void set_the_date(char *stemp)
{
    struct tm tim;

    tm_rd(&tim);
    tim.tm_year = (stemp[8] -0x30) * 10 + (stemp[9] -0x30);
    tim.tm_mday = (stemp[3] -0x30) * 10 + (stemp[4] -0x30);
    tim.tm_mon  = (stemp[0] -0x30) * 10 + (stemp[1] -0x30);
    tim.tm_wday = (stemp[11] -0x30);
    tm_wr(&tim);
}

/************************************************************
 void decimal(float value, char *buf,ndecimal)
    - convert a float value to asci including decimal point
    - corrected sample 9-7-93
    -             works with small number and - .
 ***********************************************************/
void decimal(float value, char *buf, int ndecimal)
{
    int i, j, k, d;

    i = ftoa(value, buf);
    i = -i;
    j = strlen(buf);
    if (i < j)
    {
        for (k = j; k > j - i; k--)
        {
            *(buf + k) = *(buf + k - 1);
        }
        *(buf + k) = '.';
        *(buf + k + ndecimal + 1) = '\0';
    }
    else
    {
        if (value >= 0.0)
        {
            d = i - j + 2;
            for (k = i + 1; k > i + 1 - j; k--)
            {
                *(buf + k) = *(buf + k - d);
            }
            for (k = 2; k < 2 + i - j; k++)
            {
                *(buf + k) = '0';
            }
            *(buf) = '0';
            *(buf + 1) = '.';
            *(buf + 1 + ndecimal + 1) = '\0';
        }
        else
        {
            d = i - j + 3;
            for (k = i + 1; k > i - j; k--)
            {
                *(buf + k) = *(buf + k - d);
            }
            for (k = 3; k < 3 + i - j + 1; k++) 
                *(buf + k) = '0';

            *(buf) = '-';
            *(buf + 1) = '0';
            *(buf + 2) = '.';
            *(buf + 2 + ndecimal + 1) = '\0';
        }
    }
}


/***********************************************************************
void get_mem(char *buf)
    get 16 bytes of data at ram location specified by the command stream
    Memory location starts at the third byte of the stream.
    For example "0 4c300" gets 16 bytes of data starting at location 0x4c300.
    Retrieved data are send to the serial port.
************************************************************************/
void get_mem(char *buf)
{
    unsigned long longAddress;
    int           i, h_nib, l_nib;
    char          mybuf[16];
    char          send_buf[49];

    buf[5] = '\0';
    longAddress = a_to_lh(buf);
    xmem2root(longAddress, mybuf, 0x10);
    for (i = 0; i < 16; i++)
    {
        h_nib = (mybuf[i] & 0xf0) >> 4;
        if (h_nib > 9) 
            send_buf[3 * i] = h_nib - 10 + 0x61;
        else 
            send_buf[3 * i] = h_nib + 0x30;
        l_nib = (mybuf[i] & 0x0f);
        if (l_nib > 9) 
            send_buf[3 * i + 1] = l_nib - 10 + 0x61;
        else 
            send_buf[3 * i + 1] = l_nib + 0x30;
        send_buf[3 * i + 2] = ' ';
    }
    send_buf[48] = '\0';
    write_tx(op_tbuf, send_buf);
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}


/*****************************************************************
void write_mem(char *buf)
   Write a byte data to ram location as specified in the stream.
   Data to write starts at the third byte of the stream.
   Address location starts at the sixth byte of the stream.
   e.g.  "0 23 4c300"  writes 0x23 to memory location 0x4c300.
********************************************************************/
void write_mem(char *buf)
{
    char          mybuf[2];
    unsigned long longAddress;
    int           h_nib, l_nib;

    buf[8] = '\0';
    longAddress = a_to_lh(buf + 3);

    h_nib = *(buf + 0);
    l_nib = *(buf + 1);

    if (h_nib >= '0' && h_nib <= '9') 
        h_nib = (h_nib - 0x30) << 4;

    else if (h_nib >= 'a' && h_nib <= 'f') 
        h_nib = (h_nib - 0x61 + 10) << 4;

    else if (h_nib >= 'A' && h_nib <= 'F') 
        h_nib = (h_nib - 0x41 + 10) << 4;

    if (l_nib >= '0' && l_nib <= '9') 
        l_nib = (l_nib - 0x30);

    else if (l_nib >= 'a' && l_nib <= 'f') 
        l_nib = (l_nib - 0x61 + 10);

    else if (l_nib >= 'A' && l_nib <= 'F') 
        l_nib = (l_nib - 0x41 + 10);

    mybuf[0] = h_nib + l_nib;
    root2xmem(mybuf, longAddress, 1);
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}

/***************************************************
unsigned long a_to_lh(char *buf)
  Convert an ASCII hex stream to its numerical value.
*****************************************************/
unsigned long a_to_lh(char *buf)
{
    int           i;
    int           j;
    unsigned long c;
    unsigned long data;

    data = 0;
    j    = strlen(buf);
    for (i = 0; i < j; i++)
    {
        c = buf[i];
        if (c <= '9' && c >= '0') 
            c = c - 0x30;

        else if (c <= 'F' && c >= 'A') 
            c = c - 0x41 + 10;

        else if (c <= 'f' && c >= 'a') 
            c = c - 0x61 + 10;

        else 
            return 0;
        data = data + (c << (4 * (j - i - 1)));
    }
    return data;
}

/*******************************************************************
 void remote_download(char *buf)
    - Write 128 bytes data to specified memory location.
    - Buf points to a character string that has 3 byte address and
     the 128 byte data.
*******************************************************************/
void remote_download(char *buf)
{
    unsigned long longAddress;
    unsigned long x, y, z;

    x = buf[0];
    y = buf[1];
    z = buf[2];
    longAddress = (x << 16) + (y << 8) + z;
    root2xmem(buf + 3, longAddress, 128);
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}

/*****************************************************************
void remote_data()
    - process data about the remote downloaded program to allow
      for execution of the new program.
****************************************************************/
void remote_data(char *buf)
{
    unsigned long x, y, z;

    x = buf[0];
    y = buf[1];
    next_bbr = (unsigned int)((x << 8) + y);
    x = buf[2];
    y = buf[3];
    z = buf[4];
    nxtBaseAddress = (x << 16) + (y << 8) + z;
    no_of_pages = buf[5];
    send_prompt(op_tbuf);
    replyOpto22(op_tbuf, strlen(op_tbuf), 0);
}

/*************************************************************************
int checkcrc(unsigned long baseAddress)
   - check the crc of a remotely downloaded file starting at the specified
   baseAddress.
   - returns 1 if CRC check is okay.
     returns 0 if there is error.
   - Global variables no_of_pages and nxt_bbr are also used (sorry!!).
   - If the CRC check is okay, the top 512 bytes of data space of the current
     program location is copied to the corresponding 512 bytes of the data space
     of next program location.
************************************************************************/
int check_crc(unsigned long baseAddress)
{
    int           i, j, k;
    char          mybuf[128];   // get 128 bytes at a time for the checksum
    char          lastblocksize; 
    unsigned int  blockout;   
    unsigned int  rootcode;  
    unsigned int  accum;
    unsigned int  data;
    int           blocks_of_128;
    unsigned long longAddress;
    unsigned long crcAddress;
    unsigned char crc_lowbyte, crc_hibyte;
    unsigned long src_bbr;
    unsigned long dest_bbr;

    if (no_of_pages == 0) 
        return 0;            // no file loaded
    if (no_of_pages > 128) 
        return 0;            // cannot exceed 128 pages

    crcAddress = (((long) next_bbr & 0xff) << 12) + 0x2305;
    xmem2root(crcAddress, mybuf, 5);
    crc_lowbyte = mybuf[0];
    crc_hibyte  = mybuf[1];
    rootcode    = (mybuf[4] << 8) + mybuf[3]; 
    rootcode    = rootcode - 0x2000 + 1; // Subtract 8k of lib code and
                                         // add 1 for last byte.
    blockout = rootcode / 128; // start of blockout
    lastblocksize = rootcode - blockout * 128;

    accum = 0;
    blocks_of_128 = no_of_pages * 32;
    for (i = 0; i < blocks_of_128; i++)
    {
        if ((i <= blockout) || (i >= 384) )
        {
            longAddress = baseAddress + i * 128L;
            xmem2root(longAddress, mybuf, 128);
            if (i == 6)
            {
                mybuf[5] = mybuf[6] = 0; // zero the crc values
            }               
            if (i == blockout)
            {
                for (j = lastblocksize; j < 128; j++) 
                    mybuf[j] = 0;
            }
        }
        else
        {
            for (j = 0; j < 128; j++) 
                mybuf[j] = 0;
        }
        accum = getcrc(mybuf, 128, accum);
    }
    if (((accum >> 8) & 0xff) == crc_hibyte && (accum & 0xff) == crc_lowbyte)
    {
        src_bbr  = inport(BBR);
        src_bbr  = (src_bbr << 12) + 0xde00;
        dest_bbr = (((long) next_bbr & 0xff) << 12) + 0xde00;
        for (i = 0; i < 4; i++)
        {
            xmem2root(src_bbr + i * 128L, mybuf, 128);
            root2xmem(mybuf, dest_bbr + i * 128L, 128);
        }
        return 1;
    }
    else 
        return 0;
}