
/*
 *  set clock from D C F 7 7
 *
 *  Peter M. Dambier  2002-10-21
 */

#include <stdio.h>

#define BDOS 224
#define COM_BASE  com_base
#define COM_IER   COM_BASE+1
#define COM_IIF   COM_BASE+2
#define COM_LCR   COM_BASE+3
#define COM_MCR   COM_BASE+4
#define COM_LSR   COM_BASE+5
#define COM_MSR   COM_BASE+6
#define COM_LATCH COM_BASE+7

static int com_base;
static int my_seg;
static int bdos_seg;
static int bdos_bx;
static char dcf[ 128 ];
static int  minutes, hours;
static int x_seg;
static int x_off;


/*
 *  BDOS call
 */
 
static int bdos_call( fun, parm )
    int fun, parm;
{
#asm
    push    es
    push    ds
    push    bp
    mov     cx,[bp+4]
    mov     dx,[bp+6]
    mov     ds,word bdos_seg_
    int     224
    pop     bp
    pop     ds
    mov     word bdos_bx_,bx
    mov     word bdos_seg_,es
    pop     es
#
}                                                                                                            


/*
 * set CP/M clock
 */

clock_set( buffer )
   char buffer[];
{
   _lmove( 17, buffer, my_seg, x_off + 32, x_seg );
}


/*
 *  set ko-timer
 */

static ko_set( time )
    int time;
{
#asm
    push    es
    push    di
;
    mov     di,40h
    mov     es,di
    mov     ax,[bp+4]
    mov     es:[di],al
;
    pop     di
    pop     es
#
}


/*
 *  get ko-timer
 */

static ko_get()
{
#asm
    push    es
    push    di
;
    mov     di,40h
    mov     es,di
    mov     ax,[bp+4]
    mov     al,es:[di]
    xor     ah,ah
;
    pop     di
    pop     es
#
}


/*
 *  delay
 */

static delay( cnt )
    int cnt;
{
    ko_set( cnt );

    while( ko_get() )
        ;
}


/*
 *  read port
 */
 
static int port_in( port )
    int port;
{
#asm
    mov     dx,[bp+4]   ;port
    in      al,dx
    xor     ah,ah
#
}
 
 
/*
 *  write port
 */
 
static int port_out( port, val )
    int port, val;
{
#asm
    mov     dx,[bp+4]   ;port
    mov     ax,[bp+6]   ;value
    out     dx,al
#
}                                                                                                            


/*
 * set baud rate
 */

set_baud( cnt )
    int cnt;
{
    port_out( COM_LCR, 0x80 );      /* acces baud rate counter */
    port_out( COM_BASE, cnt );      /* low baud count */
    port_out( COM_IER, cnt >> 8 );  /* high baud count */
    port_out( COM_LCR, 0x00 );      /* access data & IER */
}


/*
 * init com port
 */

com_init( port )
{
   int state;

   com_base = port;
   set_baud( 11520 );           /* 10 baud */
   port_out( COM_LCR, 0x06 );   /* 7 bit + 2 stop bits */
   port_out( COM_MCR, 0x02 );   /* -DTR +RTS */
   port_out( COM_IER, 0x00 );   /* no ints */
   delay( 37 );
   port_out( COM_MCR, 0x02 );   /* -DTR, +RTS, no ints */
   
   state = 0;

   while( state == 0 )
   {
       state  = port_in( COM_LSR ); 
       state &= 0x001f;
   }
}


/*
 * read DCF77 frame
 */

rd_dcf( buf )
  char *buf;
{
   int state, cnt;
   char xchar;


   /* read chars / timeout = 1 sec */

   cnt = 0;
   ko_set( 27 );  
   
   while( ko_get() && cnt < 72 )
   {
       state  = port_in( COM_LSR ); 
       state &= 0x001f;
       
       if( state == 1 )
       {
           /* we have data */

           ko_set( 27 );
           cnt++;

           xchar  = port_in( COM_BASE );
           
           xchar &= 0x63;

           if(      xchar == 0x63 )
              xchar = '0';
           else if( xchar == 0x62 )
              xchar = '1';
           else
              xchar = '?';
           
           *buf++ = xchar;
           continue;
       }

       if( state != 0 )
       {
           /* we have an error */

           ko_set( 27 );
           cnt++;

           xchar  = port_in( COM_BASE );
           
           xchar &= 0x63;

           if(      xchar == 0x63 )
              xchar = '0';
           else if( xchar == 0x62 )
              xchar = '1';
           else
              xchar = '?';

           *buf++ = xchar;

           if( state & 2 ) 
               *buf++ = 'o';
           
           if( state & 4 ) 
               *buf++ = 'p';
           
           if( state & 8 ) 
               *buf++ = 'f';
           
           if( state & 16 ) 
               *buf++ = 'b';
           
           continue;
       }
   }

   *buf = '\0';
}


/*
 *  get number from BCD bits
 */

int get_bits( cp, len )
   int len;
   char *cp;
{
   int val;
   static char posv[ 9 ] = { 1, 2, 4, 8, 10, 20, 40, 80 };
   char *vp;


   if( len > 8 )
      return( -1 );

   vp  = posv;
   val = 0;

   while( len-- )
   {
      if( *cp == '1' )
         val += *vp;
      else if( *cp != '0' )
         return( -1 );

      cp++;
      vp++;
   }

   return( val );
}


/*
 *  get parity from BCD bits
 */

int get_parity( cp, len )
   int len;
   char *cp;
{
   int val;


   if( len > 30 )
      return( -1 );

   if( len <= 1 )
      return( -1 );

   val = 0;

   while( len-- )
   {
      if( *cp == '1' )
         val++;
      else if( *cp != '0' )
         return( -1 );

      cp++;
   }

   return( val & 1 );
}


/*
 *  decode dcf77 info
 */

int get_info( dcf )
   char *dcf;
{
   int seconds, days, months, years, weekdays, len;
   char *cp;
   char buffer[ 80 ];
   int parity;


   len      = strlen( dcf );

   if( len < 40 )
      return( 0 );

   cp       = dcf;
   cp      += len;
   parity   = 0;


   /* * parity 3 * */

   cp      -= 9;
   years    = get_bits( cp, 8 );

   cp      -= 5;
   months   = get_bits( cp, 5 );

   cp      -= 3;
   weekdays = get_bits( cp, 3 );

   cp      -= 6;
   days     = get_bits( cp, 6 );

   if( get_parity( cp, 23 ) )
      parity += 4;

   /* * parity 2 * */

   cp      -= 7;
   hours    = get_bits( cp, 6 );

   if( get_parity( cp, 7 ) )
      parity += 2;

   /* * parity 1 * */

   cp      -= 8;
   minutes  = get_bits( cp, 7 );

   if( get_parity( cp, 8 ) )
      parity += 1;

   printf( "%02d-%02d-%02d %02d:%02d %d\n",
      years, months, days, hours, minutes, weekdays );

   if( parity )
      return( 0 );

   sprintf( buffer, "%02d/%02d/%02d,%02d:%02d:00",
      months, days, years, hours, minutes );

   clock_set( buffer );

   printf( ">>>%s<<<\n", buffer );

   return( 1 );
}


/*
 *  * * *
 */

main( argc, argv )
    int argc;
    char *argv[];
{
    char par1[30], par2[30], par3[30], buffer[256];
    int port, inifile;


    /* get parameters */

    port = 0;

    inifile = fopen( "GSM.INI", "r" );

    if( inifile == 0 )
    {
       printf( "Error: Could not open (GSM.INI).\n" );
       exit( 1 );
    }

    while( fgets( buffer, 256, inifile ) != 0 )
    {
       if( buffer[0] == '#' )
          continue;

       sscanf( buffer, "%s %s %s", par1, par2, par3 );

       if( strcmp( "=", par2 ) != 0 )
          continue;

       if( strcmp( "DCFPORT", par1 ) == 0 )
          port = atoi( par3 );
    }

    fclose( inifile );

    if( port == 0 )
    {
        printf( "Error: DCFPORT = 0\n" );
        exit( 1 );
    }


    /* init COM port */

    my_seg   = _showds();
    bdos_seg = my_seg;
    bdos_call( 0x31, 0, 0 );
    x_seg    = bdos_seg;
    x_off    = bdos_bx;
    bdos_seg = my_seg;

    com_init( port );

    for(;;)
    {
       if( csts() )
         break;

       rd_dcf( dcf );
       printf( ">%s<\n", dcf );

       if( get_info( dcf ) )
          break;
    }
}

