/* =====================================================================
* pgm-sx.c
* A console based C program used to generate prom images for the GE
* Phoenix SX radios (2 and 16 channel non scaning).
* This program was written by Lawrence Glaister VE7IT Jan 24 2004
*
* To use edit data in structures below and compile using:
* gcc -O2 -Wall pgm-sx.c -o pgm-sx
*
* This should compile under windows, but you will need the expensive
* visual C development system installed.
* See GE document LBI 31266.pdf for reference information.
*
*=======================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*==========  MODIFY THE VALUES BELOW TO SETUP YOUR RADIO ================*/
/* radio wide definitions */
# define CCT 4          /* 0..7 carrier controlled timer n*30sec + 30sec */
# define REF 13.2       /* ref oscialltor in Mhz */
# define SPACE 0.005    /* channel spacing in Mhz */
# define REFCODE  2     /* 2=13.2mhz ref osc and ch spacing of 5khz */
/* note. each channel can have its own refcode but unless you need 12.5khz */
/* channel spacing, we simplified the program to used one reference divider code */

/* for each channel, one has to define the following data... */
/* the first 8 are mode A and the second 8 are mode B        */
struct CH{
    int cg_type;    /* see table 2 in LBI doc, 0 = none or PL */
    float rx;       /* rx freq ie 147.280 */
    int rxcg_code;  /* see table 3A in LBI doc 100hz = 0xB0, 141.3 = 0x51 */
    int cct;        /* TX timeout timer enable for ch 0=none,8=enabled */
    float tx;       /* TX freq ie 147.880 */
    int txcg_code;  /* see table 3A in LBI doc 100hz = 0xB0, 141.3 = 0x51 */
}channels[16] = {
/*cg    RX freq   cgcode   CCT  TX freq    cgcode */
{ 2,    147.240,    0x51,   8,  147.840,    0x51},     /* VE7KU */
{ 2,    147.150,    0x51,   8,  147.750,    0x51},     /* VE7RPA */
{ 0,    147.000,    0xB0,   8,  146.400,    0xB0},     /* VE7RWC */
{ 2,    147.280,    0x11,   8,  147.880,    0x51},     /* VE7PQA */
{ 2,    147.080,    0x51,   8,  147.680,    0x51},     /* Parksville */
{ 0,    147.540,    0x00,   8,  147.540,    0x00},     /* Simplex */
{ 0,    147.420,    0x00,   8,  147.420,    0x00},     /* Simplex */
{ 0,    146.520,    0x00,   8,  146.520,    0x00},     /* Simplex */

{ 2,    169.590,    0xB0,   8,  169.590,    0xB0},     /* PAFD */
{ 2,    169.740,    0xB0,   8,  169.740,    0xB0},     /* PAFD */
{ 0,    155.460,    0x00,   8,  155.460,    0x00},     /* OFCBC */
{ 0,    154.710,    0x00,   8,  150.305,    0x61},     /* Fire Dispatch Cameron Repeater */
{ 0,    149.495,    0x00,   8,  149.495,    0x00},     /* AVRS */
{ 0,    142.365,    0x00,   8,  138.375,    0x11},     /* EHS  */
{ 0,    140.790,    0x00,   8,  141.720,    0x00},     /* RCMP CH F */
{ 0,    164.190,    0x00,   8,  164.190,    0x00}      /* FIFT tankers*/

#if 0
/*cg    RX freq   cgcode   CCT  TX freq    cgcode */
{ 0,    141.090,    0x00,   8,  0.00000,    0x00},     /* Parksville RCMP*/
{ 0,    140.280,    0x00,   8,  0.00000,    0x00},     /* Nanaimo RCMP*/
{ 0,    143.415,    0x00,   8,  0.00000,    0x00},     /* Hiways */
{ 0,    144.390,    0x00,   8,  144.390,    0x00},     /* APRS packet*/
{ 0,    144.970,    0x00,   8,  144.970,    0x00},     /* Local Packet */
{ 2,    145.290,    0x51,   8,  144.690,    0x51},     /* Port Alberni */
{ 0,    145.350,    0x00,   8,  144.750,    0x00},     /* VE7RBY Burnaby */
{ 0,    145.370,    0x00,   8,  144.770,    0x00},     /* Mira Parksville */

{ 2,    145.430,    0xB0,   8,  144.830,    0xB0},     /* Nanaimo Backbone */
{ 0,    146.680,    0x00,   8,  146.080,    0x00},     /* Nanaimo */
{ 2,    147.000,    0xB0,   8,  146.400,    0xB0},     /* Ucluelet */
{ 2,    147.080,    0x51,   8,  147.680,    0x51},     /* Parksville Backbone */
{ 0,    147.180,    0x00,   8,  147.780,    0x00},     /* Kenny's Rptr */
{ 2,    147.240,    0x51,   8,  147.840,    0x51},     /* Port Alberni */
{ 2,    147.280,    0x11,   8,  147.880,    0x51},     /* Parksville 123/141*/
{ 0,    147.320,    0x00,   8,  147.980,    0x00}      /* Saltspring */
#endif

};

/*==========  MODIFY THE VALUES ABOVE TO SETUP YOUR RADIO ================*/

char version[] = {"26 Jan,2004"};
char prom[256];     /* ram image of 256 nibble eeprom */

/*======================================================================
* calc_rxfreq_prom()
* This routine computes rx frequency data for prom.
*=======================================================================
*/
void calc_rxfreq_prom(void)
{
    int ch;
    int divisor;
    int offset;
    
    /* rx freq injection is rx_freq + 45mhz. Divider is  */
    /* injection freq / ch spacing                       */
    /* ie (145.52 +45) / 0.005 = 38304                   */
    for ( ch = 0; ch < 16; ch++)
    {
        if ( ch < 8 )       /* compute prom offset for start of rx ch data */
            offset = (0x20 + (ch * 0x20)) % 256;         /* mode A rx locns */
        else
            offset = (0x28 + ((ch - 8) * 0x20)) % 256;   /* mode B rx locns */

        if ( channels[ch].rx > 0.0 )
        {
            divisor = ((channels[ch].rx  + 45.0)+ SPACE/2.0) / SPACE;
            /* printf("off= %04X, divisor= %d\n",offset, divisor); */
            prom[offset+0] = channels[ch].cg_type;
            prom[offset+1] = REFCODE + ((divisor & 0x8000) >> 12);
            prom[offset+2] = (divisor & 0x780) >> 7;
            prom[offset+3] = (divisor & 0x7800) >> 11;
            prom[offset+4] = divisor & 0xf;
            prom[offset+5] = ((divisor  & 0x30) >> 4) + ((divisor & 0x40) >> 3);
            prom[offset+6] = (channels[ch].rxcg_code & 0xf0) >> 4;
            prom[offset+7] = channels[ch].rxcg_code & 0x0f;
        }
    }
}

/*======================================================================
* calc_txfreq_prom()
* This routine computes tx frequency data for prom.
*=======================================================================
*/
void calc_txfreq_prom(void)
{
    int ch;
    int divisor;
    int offset;
        
    /* tx freq injection is tx_freq . Divider is  */
    /* injection freq / ch spacing                */
    /* ie 145.52 / 0.005 = 29304                  */
    for ( ch = 0; ch < 16; ch++)
    {
        if ( ch < 8 )       /* compute prom offset for start of tx ch data */
            offset = (0x30 + (ch * 0x20)) % 256;         /* mode A tx locns */
        else
            offset = (0x38 + ((ch - 8) * 0x20)) % 256;   /* mode B tx locns */

        if ( channels[ch].tx > 0.0 )
        {
            divisor = (channels[ch].tx + SPACE/2.0) / SPACE;
            /* printf("off= %04X, divisor= %d\n",offset, divisor); */
            prom[offset+0] = channels[ch].cct;
            prom[offset+1] = (divisor & 0x8000) >> 12;
            prom[offset+2] = (divisor & 0x780) >> 7;
            prom[offset+3] = (divisor & 0x7800) >> 11;
            prom[offset+4] = divisor & 0xf;
            prom[offset+5] = ((divisor  & 0x30) >> 4) + ((divisor & 0x40) >> 3);
            prom[offset+6] = (channels[ch].txcg_code & 0xf0) >> 4;
            prom[offset+7] = channels[ch].txcg_code & 0x0f;
        }
    }
}


/*======================================================================
* calc_specials_prom()
* This routine computes special (#ch, TOT) data for prom.
*=======================================================================
*/
void calc_specials_prom(void)
{
    int ch;
    int num_chA = 0;
    int num_chB = 0;
    
    /* number of channels seems to be highest valid channel in Bank */
    for ( ch = 0; ch < 16; ch++)
    {
        if ( channels[ch].rx > 0.0 )
        {
            if( ch < 8)
                num_chA = ch + 1;
            else
                num_chB = ch + 1 - 8;
	    }
	}
    printf("Found %d channels defined for mode A and %d for mode B\n",num_chA,num_chB);

    printf(" 0xF1 = num ch A: %02X + %02X\n",prom[0xf1],(num_chA -1));
    /* set number of channels in mode A  (combines with ch 7 freq data) */
    prom[0xf1] = (prom[0xf1] + (num_chA -1)) & 0xf;

    printf(" 0x19 = num ch B: %02X + %02X\n",prom[0x19],(num_chB -1));
    /* set number of channels in mode B  (combines with ch 8 freq data) */    
    prom[0x19] = (prom[0x19] + (num_chB -1)) & 0xf;

    printf(" 0x11 = TOT: %02X + %02X\n",prom[0x11],CCT);
    /* set timeout timer duration for radio (combines with ch8 data)*/
    prom[0x11] = (prom[0x11] + CCT) & 0xf;
}


/*======================================================================
* init_prom()
* This routine sets up an image of a freq prom with no channels defined
*=======================================================================
*/
void init_prom(void)
{
    int i;
    
    memset(&prom[0],0,sizeof(prom));
    /* Disable all channels rx and tx*/
    for ( i = 5; i <= 255; i += 8 )
        prom[i] = 0x0f;
 
}

/*======================================================================
* dump_prom()
* This routine writes the prom image out to an intel hex format text 
* file.
*=======================================================================
*/
void dump_prom(void)
{
    FILE *fp;
    char cksum;
    int i;
    
    cksum = 0;
    
    fp = fopen("prom.hex","wt");
    if ( fp == NULL )
    {
        printf("opps, unable to open prom.hex for writing!\n");
        exit( -1);
    }
    else
    {
        for ( i=0; i<256; i++)
        {
            if ( i % 16 == 0 )
            {
                fprintf(fp,":10%04X00",i);  /* 16 bytes data,addr, rectype=0 */
                cksum = (0x10 + (i>>8) + i) & 0xFF;
            }
            fprintf(fp,"%02X",prom[i]);         /* write 1 data byte in hex */
            cksum += prom[i];
            
            if ( i % 16 == 15 )             /* last byte on line */
            {
                fprintf(fp,"%02X\n",-cksum & 0xFF);    /* write cksum, newline */
            }       
        }
        fprintf(fp,":00000001FF\n");        /* end of file marker */
        fflush(fp);
        fclose(fp);
        printf("Success: File >>prom.hex<< written\n");
    }
}

/*======================================================================
* main()
* Build a prom image for a 256x4 X22C12 based on table of 16 freqs.
*=======================================================================
*/
int main( int argc, char **argv)
{
    printf("Welcome to pgm-sx by Lawrence Glaister VE7IT\n");
    printf("Version: %s\n",version);
    printf("To use this program, you must edit pgm-sx.c and setup the\n");
    printf("channel and radio specific items.\n");
    printf("Then rebuild and run the program to generate the file prom.hex\n");
    printf("   for example for you linux users....\n");
    printf("   gedit pgm-sx.c\n");
    printf("   gcc -O2 -Wall pgm-sx.c -o pgm-sx\n");
    printf("   ./pgm-sx\n");
    
    init_prom();            /* empty prom image */
    calc_rxfreq_prom();     /* fill in rx freqs and info*/
    calc_txfreq_prom();     /* fill in tx freqs and info */
    calc_specials_prom();   /* fill in # ch and TOT */
    dump_prom();            /* generate the intel hex image */

    return 0;
}

