/*
	com1rem.c

   Sample Program for com1 and com2 of a standard PC serial card mounted
   on the PC104 Bus or the ISA Bus.

       This program demonstrates the use of a standard PC serial 
       communication port com1 with the Z104 or the ZISA boards with more
       than 32 kbytes of RAM.
       
       The program does the following:

       (1) remote monitoring with a pc dumb with or without the
       use of a modem and (2) master to slave networking for
       localized control.

       A modem card can be directly interfaced to the PC/104 bus or to the
       ISA bus. 

       A null modem is required for direct RS232 link to a PC serial port,
       since both serial ports are now DTE's (Data Terminal Equipment).

       The master to slave communication is thru the RS485 link of the
       Z104 cards.

       Hardware connection for RS485 link (z104 port 1):
        master                       slave (or slaves)
        RS485+                        RS485+
        TX+  |  -----------------  |  TX+
        RX+  |                     |  RX+

        RS485-                        RS485-
        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.

       If ismodem = 1, the master monitors for a modem link. Once contact is
       established, the pc terminal would just be a dumb terminal passing asci
       character thru the phone lines to the master. The master would wait for
       a command line ending in CR<ENTER> and would process the command
       accordingly. With ismodem = 0, the pc dumb terminal would right away be
       able to communicate with the master.

       For this demonstration the following coded command line is used:

       <first byte>  -   target, '0' for master
                                 '1' for slave 1
                                 '2' for slave 2
                                 'a' for master and all the slaves
      <second byte>  - a space delimiter
      <third byte> and others
            - 'm' for menu
            - 'h' for help
            - 'v' for display of monitored variables
            - 't' for time and date
            - 'd' for download of data using the xmodem protocol
                  - can only download from the master.
            - 'u xxxxx' for upload of binary file to ram location starting
                 at hex address xxxxx.
            - 'l' to start logging and stop logging data
            - 'i' try to establish contact with 9 slaves
            - 'x xxxxx'  read 16 bytes of data starting at hex address xxxxx
            - 'w yy xxxxx' write a byte of data to hex address xxxxx
            - 'z' compute crc of the uploaded program to ram and jump to the
                  remote uploaded program if program is valid

      For example, to ask for menu from master > 0 m  .
                   to ask for time from master > 0 t  .
                   to set the master time      > 0 s t 10:23:23 .
                   to set time for slave 4     > 4 s t 10:23:12 .
                   to read ram memory at 0xXXXXX
                        of the master          > 0 x 401a3
                   to read ram memory at 0x80300
                        of slave no. 2         > 2 x 80300
                   to read time from the master and
                       the nine slaves         > a t
                   to upload file to slave no.2
                      starting at address 0x8c000 > 2 u 8c000
                   to download file from the master > 0 d
         Not all the commands has the a (all) option.

        This program also supports the xmodem file protocol.
        To receive file on the Z180 board:
            1. type "'target' u  xxxxx" for receiving file.
            2. back to your pc, type <PgUp> and choose xmodem protocol and
                enter the file to transfer.
            3. For this sample program, we allow for remote downloading of
               executable code into ram location at 0x8c000. We will be
               providing a more comprehensive write up that would explain its
               functionality. The user may also need an upgraded dynamic C
               library to run remote downloaded program.
        To transmit file from the master only:
            1.  type "0 d" for downloading file.
            2. back to your pc, type <PgDwn> and choose the xmodem protocol
                and enter the file to store.
        If you took too long to initiate the protocol from the pc, the
        z180 board may timeout. Timeout procedures are also embedded in many
        areas where the Z180 board would be waiting for a pc response. In
        any case, a time out will just return to the menu mode.

        The user has to provide their own handling for the uploaded file.
        For this demonstration, the log on_off toggle would log time and
        date every thirty second to a logfile. An xmodem down request would
        send down the logged file.


 The following libraries are needed: modem232.lib, com232.lib and network.lib.
 Drivers.lib has to be current also ( 5/17/93 or later).
*/

#use "default.h"

#if (BOARD_TYPE!=Z104ISA)
#fatal "This program only runs on the Z104 or ZISA boards."
#endif

/**********************************************
CONTROL PARAMETERS
***********************************************/

#define IBAUD   9600         /* baud rate
                                with modem either 2400 or 1200
                                without modem => 19200, 9600, 4800, etc  */
#define TBUFSIZE 384         // size of transmit buffer
#define RBUFSIZE 384         // size of receive buffer

#define log_size  1024       // mamixum byte for logging data
char ismodem = 0;            // set to 1 if you want modem
char isecho  = 1;            // set to 1 if you want character echoed back to the pc

char op_tbuf[255], op_rbuf[255];
struct s_array
{                            // sample data structure for monitoring
    char name[20];
    char tag;
    float value;
};

struct s_array vdata[4]; /* define as global so as not to keep passing back
                            and forth                                    */
char modata[150];            // scratch array to write uploaded file
long alarm_at;     // set alarm time, i.e. seconds elapsed from jan 1,1980
long alarm_too;
int  log_data;               // toggle periodic log data or not
int  alarm_offset;           // next alarm offset
char data_log[log_size];     // log file, xmodem down on request
char data_test[log_size];    // test file for parsed uploaded file

int no_of_pages;
unsigned int next_bbr;
unsigned long nxtBaseAddress;
extern void Dcom1_circ_int(void);

// Function prototypes
void process_data(char *buf);
void log_or_unlog(void);
void send_file_down(void);
void rec_file_up(char *buf, int dest);
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 snd_time_date(int mode);
void set_the_time(char *buf);
void set_the_date(char *buf);
void decimal(float value, char *buf, int ndecimal);
int  store_message(char *message);
void talk_to_master(char *buf);
void init_9slaves(void);
void also_do_the_slaves(char *buf);
void talk_to_a_slave(char d_slave, char *s_msg);
void get_mem(char *buf);
void write_mem(char *buf);
unsigned long a_to_lh(char *buf);
int  check_crc(unsigned long baseAddress);
int  parse_data(char *mydata, int nth_block, unsigned long baseAddress, 
     int dest);

main()
{
    int  ercode;
    int  ok_count;
    struct tm aatime;
    char tbuf[TBUFSIZE];     // transmit buffer
    char rbuf[RBUFSIZE];     // receive buffer
    char buf[20];         // dummy buffer for receiving a complete command

    op_kill_z1();
    alarm_offset = 30;
    tmc_rd(&aatime);
    alarm_at = mktime(&aatime); /* read current absolute time in 
                                   seconds from Jan 1, 1980     */
    alarm_at = alarm_at + alarm_offset; // alarm after 30 secs
    log_data = 0;

                             // initialize dummy data for demo
    vdata[0] = makedata("(a) data1       ", 'a', 0.5);
    vdata[1] = makedata("(b) data2       ", 'b', 23.00);
    vdata[2] = makedata("(c) data3       ", 'c', 534.123);
    vdata[3] = makedata("(d) data4       ", 'd', 6456.32);

    // Initialize rs232 serial port for pc to master comm
    if( Dinit_com1(rbuf, tbuf, RBUFSIZE, TBUFSIZE, 4, 
        IBAUD / 1200, ismodem, isecho) == -1)
    {
        printf("COM1 is not found!!!\n");
        while(1)
            runwatch();     // do not proceed with the program
    }
    
    // Initialize opto22 network
    op_init_z1(19200 / 1200, op_rbuf, 0);

    if (!ismodem)            // If no modem, you have to service your 
    {                        // own screen at power up
        Dcom1send_prompt();
        main_menu();
    }

    for (;;)    // endless loop for monitoring new command line
    {
        
        if ((ercode = Dread_com1(buf, ENTER)) != 0)
        {
            Dcom1send_prompt();
            process_data(buf);
        }
        tmc_rd(&aatime);
        alarm_too = mktime(&aatime);
        if (log_data && alarm_at <= alarm_too)
        {
            alarm_at = alarm_too + alarm_offset;
            snd_time_date(0);
        }
    }
}


/*********************************************************************
void process_data()
  - process string command from the pc and decide whether the message
  is for the master or for any slave.
***********************************************************************/
void process_data(char *buf)
{
    if (*(buf) == '0' || *(buf) == 'a' || *(buf) == 'A' || *(buf + 2) == 'u')
        talk_to_master(buf);
    else if (*(buf) >= '1' && *(buf) < '9') 
        talk_to_a_slave(*(buf), buf + 2);
    else
    {
        Dwrite_com1("No destination", 14);
        Dcom1send_prompt();
    }
}


/******************************
void log_or_unlog()
   - toggle on off periodic logging of data
*********************************/
void log_or_unlog(void)
{
    int  i;

    if (log_data)
    {
        log_data = 0;
        Dwrite_com1("data logging turn off", 21);
    }
    else
    {
        log_data = 1;
        for (i = 0; i < 1024; i++) 
            data_log[i] = 0x30;
        data_log[0] = 0;
        Dwrite_com1("data logging turn on", 20);
    }
    Dcom1send_prompt();
}

/**************************************************************************
void send_file_down()
     - Initiate protocol for sending a file to the dumb termimal via xmodem
***************************************************************************/
void send_file_down(void)
{
    int j, status;

    status = Dxmodem_com1down(data_log, 4);
    switch (status)
    {
        case 0:
            Dwrite_com1("TIMEOUT", 7);
            break;
        case 1:
            Dwrite_com1("TRANSFER OKAY", 13);
            break;
        case 2:
            Dwrite_com1("CANCELLED", 9);
            break;
        default:
            break;
    }
    Dcom1send_prompt();
    Dreset_com1rbuf();
}


/***********************************************************************
void rec_file_up()
    Initiate protocol for receiving file from a PC using xmodem.
************************************************************************/
void rec_file_up(char *buf, int dest)
{
    char mydata[10];

    nxtBaseAddress = a_to_lh(buf + 2);
    next_bbr = (unsigned int)(((nxtBaseAddress - 0x2000) >> 12) | 0xe200);
                             // get next_bbr on file up transfer

    switch (Dxmodem_com1up(nxtBaseAddress, &no_of_pages, dest, parse_data))
    {
        case 0:
            Dwrite_com1("TIMEOUT", 7);
            break;
        case 1:
            Dwrite_com1("TRANSFER OKAY", 13);

            // here user can do "whatever" to the uploaded file
            if (dest != 0)
            {
                Dcom1send_prompt();
                             // mydata[0] = dest;
                             // mydata[1] = 7;
                mydata[0] = 'd';
                mydata[1] = (next_bbr >> 8) & 0xff;
                mydata[2] = next_bbr & 0xff;
                mydata[3] = (char)((nxtBaseAddress >> 16) & 0xff);
                mydata[4] = (char)((nxtBaseAddress >> 8) & 0xff);
                mydata[5] = (char)(nxtBaseAddress & 0xff);
                mydata[6] = no_of_pages;
                if (sendOp22(dest, mydata, 7, 8) < 0) 
                    Dwrite_com1("bad slave", 9);
                else 
                    Dwrite_com1("Slave download Okay", 19);
            }
            break;
        case 2:
            Dwrite_com1("CANCELLED", 9);
            break;
        default:
            break;
    }
    Dcom1send_prompt();
    Dreset_com1rbuf();
}


/*********************************
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;
    }
}

/***********************************
void store_data(char *buf,int itag)
***********************************/
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;
        }
    }
    Dwrite_com1("No matching tag", 15);
    Dcom1send_prompt();
}


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

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

    Dwrite_com1("tag name value", 14);
    Dcom1send_prompt();
    for (i = 0; i < 4; i++)
    {
        Dwrite_com1(vdata[i].name, 16);
        decimal(vdata[i].value, s_pad, 4);
        Dwrite_com1(s_pad, strlen(s_pad));
        Dcom1send_prompt();
    }
}

/*********************************************
void main_menu()
   Display the main menu
***********************************************/
void main_menu(void)
{
    Dwrite_com1("Main MenuTEST", 13);
    Dcom1send_prompt();
    Dwrite_com1("(m) main menu", 13);
    Dcom1send_prompt();
    Dwrite_com1("(v) variables", 13);
    Dcom1send_prompt();
    Dwrite_com1("(t) time/date", 13);
    Dcom1send_prompt();
    Dwrite_com1("(l) log or unlog data", 21);
    Dcom1send_prompt();
    Dwrite_com1("(i) init and find the slaves", 28);
    Dcom1send_prompt();
    Dwrite_com1("(x) read from memory location", 29);
    Dcom1send_prompt();
    Dwrite_com1("(w) write to memory location ", 29);
    Dcom1send_prompt();
    Dwrite_com1("(z) jump to remote downloaded program", 37);
    Dcom1send_prompt();
    Dwrite_com1("(u) upload   ", 13);
    Dcom1send_prompt();
    Dwrite_com1("(d) download ", 13);
    Dcom1send_prompt();
    Dwrite_com1("(h) help     ", 13);
    Dcom1send_prompt();
}

/**************************************
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;
}

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


/**************************************************
void snd_time_date(int mode)
   - read 72421 timer chip, parse the data into readable string.
   - mode = 0, log string to ram
     mode = 1, send string to serial port
****************************************************/
void snd_time_date(int mode)
{
    struct tm tim;
    char      s_pad[25];

                             // read the real time clock
    tmc_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] = '-';
    s_pad[11] = '1';
    s_pad[12] = '9';
    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';
    if (mode) 
        Dwrite_com1(s_pad, 24);
    else
    {
        store_message(s_pad);
        store_message("\r\n");
    }
}


/******************************************************************
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:12:12
********************************************************************/
void set_the_time(char *stemp)
{
    struct tm tim;
    
    tmc_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);
    tmc_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;

    tmc_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);
    tmc_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';
        }
    }
}

/******************************************************************
int store_message(char *message)
   - store a string message into the storage file.
   - the user can then retrieve the file using xmodem file protocol
   - for demo, if the end of the storage array is reached, the array
     goes back to the top of the array, a 0 is return to designate
     restarting of the array.
   - this circular logging could be used for storing periodic message
     and at the same time writing over non-current data
 ****************************************************************/
int store_message(char *message)
{
    int i;

    if (strlen(message) + strlen(data_log) + 1 < 512)
    {
        strcat(data_log, message);
        return 1;
    }
    else
    {
                             // do not write more to file, if filled
        return 0;
    }
}


/*********************************************
void talk_to_master()
    - pc talks to the master
**********************************************/
void talk_to_master(char *buf)
{
    int i;

    Dwrite_com1("master", 6);
    Dcom1send_prompt();

    switch (*(buf + 2))
    {
        case 'v':
            variables_menu();
            break;
        case 't':
            snd_time_date(1);
            Dcom1send_prompt();
            if (*(buf) == 'a' || *(buf) == 'A') 
                also_do_the_slaves(buf);
            break;
        case 's':
            set_data_or_time(buf + 4);
            Dcom1send_prompt();
            if (*(buf) == 'a' || *(buf) == 'A') 
                also_do_the_slaves(buf);
            break;
        case 'm':
            main_menu();
            break;
        case 'h':
            help_menu();
            break;
        case 'd':
            send_file_down();
            break;
        case 'u':
            rec_file_up(buf + 2, *(buf) - 0x30);
            break;
        case 'l':
            log_or_unlog();
            break;
        case 'i':
            init_9slaves();
            break;
        case 'x':
            get_mem(buf + 2);
            Dcom1send_prompt();
            break;
        case 'w':
            write_mem(buf + 2);
            Dcom1send_prompt();
            break;
        case 'z':
            if (check_crc(nxtBaseAddress) == 1)
            {
                op_kill_z1();
                
                /* turn off timer interrupts; serial interrupts;
                   INTs' interrupts; DMA interrupts; SIO's,CTC2,CTC3 
                   for LG; and PIO's for LG,TG, LPLC                */
                resetZ180Int();
                newbbr(0x2200, next_bbr);
            }
            else
            {
                Dwrite_com1("bad crc", 7);
                Dcom1send_prompt();
            }
            break;

        default:
            Dwrite_com1("bad command", 11);
            Dcom1send_prompt();
            break;
    }
}


/**************************************************************
void init_9slaves()
    - look for slaves in the rs485 network,
       default to 1-9 slaves for this sample
***************************************************************/
void init_9slaves(void)
{
    int i;
    
    for (i = 1; i <= 9; i++)
    {
        Dwrite_com1("slave ", 6);
        Dwrite_com11ch(i + 0x30);
        if (sendOp22(i, "i", 1, 4) < 0)
        {
            Dwrite_com1(" not found", 10);
        }
        else
        {
            Dwrite_com1(" slave found", 12);
        }
        Dcom1send_prompt();
    }
}


/************************************
void also_do_the_slaves()
   - send command line to all the slaves
*****************************************/
void also_do_the_slaves(char *buf)
{
    int i;
    
    for (i = 1; i <= 9; i++)
    {
        talk_to_a_slave(i + 0x30, buf + 2);
    }

}


/*******************************************
void talk_to_a_slave
   - pc talks to a slave
*********************************************/
void talk_to_a_slave(char d_slave, char *s_msg)
{
    char send_buffer[255];   // traffic flow send buffer
    int  ndelay;
    int  ercode;

                             // send_buffer[0] = d_slave - 0x30;
                             // send_buffer[1] = strlen(s_msg);
                             // strcpy(send_buffer+2,s_msg);
    Dwrite_com1("slave ", 6);
    Dwrite_com11ch(d_slave);
    Dcom1send_prompt();
    if (s_msg[0] == 'z')
    {
        ndelay = 100;
    }
    else 
        ndelay = 8;
    ercode = sendOp22(d_slave - 0x30, s_msg, strlen(s_msg), ndelay);
    if (ercode == -1)
    {
        Dwrite_com1("bad slave ", 9);
    }
    else if (ercode == -2)
    {
        Dwrite_com1("bad crc on return", 17);
    }
    else
    {
        Dwrite_com1(op_rbuf + 1, op_rbuf[0] -2); // do not show the crc
    }
    Dcom1send_prompt();
}


/***********************************************************************
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[48];
    
    longAddress = a_to_lh(buf + 2);
    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] = ' ';
    }

    Dwrite_com1(send_buf, 48);
    Dcom1send_prompt();
}

/*****************************************************************
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;

    longAddress = a_to_lh(buf + 5);

    h_nib = *(buf + 2);
    l_nib = *(buf + 3);

    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);
    Dcom1send_prompt();
}


/***************************************************
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;
}

/*************************************************************************
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 are 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 byte block 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;
}


/*****************************************************************************
int parse_data(char *mydata, int nth_block, unsigned long baseAddress, int dest)
   - parse a remotely downloaded binary file into the master's ram location
     or to any designated slave.
******************************************************************************/
indirect int parse_data(char *mydata, int nth_block,
             unsigned long baseAddress, int dest)
{
    unsigned long longAddress;
    unsigned long temp;
    char send_buf[255];
    int i;

    longAddress = baseAddress + 128L * (nth_block - 1);
    temp = longAddress;
 
    if (dest == 0)
    {
        root2xmem(mydata, longAddress, 128);
        return 1;
    }
    else
    {
                             // send_buf[0] = dest;
                             // send_buf[1] = 132;
        send_buf[0] = 'r';
        send_buf[1] = (char)((temp >> 16) & 0xff);
        send_buf[2] = (char)((temp >> 8) & 0xff);
        send_buf[3] = (char)(temp & 0xff);
        for (i = 0; i < 128; i++) 
            send_buf[4 + i] = mydata[i];
        if (sendOp22(dest, send_buf, 132, 8) < 0) 
            return 0;
        else 
            return 1;
    }
}
