[comp.sys.amiga] Amiga real-time clock plans - part 2/2: sources

gclark@utcsri.UUCP (09/06/87)

Here is part 2 of my Amiga real-time clock design, containing the sources
for the clock software.  

Graeme Clark -- Dept. of Computer Science, Univ. of Toronto, Canada M5S 1A4
{allegra,cornell,decvax,ihnp4,linus,utzoo}!utcsri!gclark
gclark@csri.toronto.edu

----------------- cut here -----------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	makefile
#	clockwr.c
#	clockrd.c
#	clocktest.c
#	nice_time.h
# This archive created: Sat Aug 29 19:59:54 1987
export PATH; PATH=/bin:$PATH
echo shar: extracting "'makefile'" '(359 characters)'
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
# Makefile for clock programs, Manx 3.4a

clockrd : clockrd.o
  ln -o clockrd clockrd.o -lc

clockwr : clockwr.o
  ln -o clockwr clockwr.o -lc

clocktest : clocktest.o
  ln -o clocktest clocktest.o -lc

clockrd.o : clockrd.c nice_time.h
  cc clockrd.c

clockwr.o : clockwr.c nice_time.h
  cc clockwr.c

clocktest.o : clocktest.c nice_time.h
  cc clocktest.c

SHAR_EOF
if test 359 -ne "`wc -c < 'makefile'`"
then
	echo shar: error transmitting "'makefile'" '(should have been 359 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'clockwr.c'" '(14906 characters)'
if test -f 'clockwr.c'
then
	echo shar: will not over-write existing file "'clockwr.c'"
else
cat << \SHAR_EOF > 'clockwr.c'
#include <stdio.h>
#include <exec/types.h>
#include <hardware/custom.h>
#include <hardware/cia.h>
#include <resources/potgo.h>
#include <devices/timer.h>
#include <functions.h>

#include "nice_time.h"

/**********************************************************************
 * clockwr -- prompt for the time and date, and set the hardware and  *
 *            software clocks                                         *
 *   usage: clockwr                                                   *
 **********************************************************************/

main()
    {
    struct nice_time tm;
    long seconds;

    void set_time(), print_time(), term_port();
    int  init_port(), input_time(), check_time(), set_sys_time();
    long nice_to_sec();

    if (init_port() != 0)
      exit(1);
    if (input_time(&tm) != -1)
      {
      if (check_time(&tm) == -1)
        {
        printf("That doesn't look like a real date to me, so I'm\n");
        printf("not going to bother to set the clocks.\n");
        }
      else
        {
        printf("Setting hardware clock..."); fflush(stdout);
        set_time(&tm);
        printf("\nSetting software clock..."); fflush(stdout);
        seconds = nice_to_sec(&tm);
        (void)set_sys_time(seconds);
        printf("\nTime set to ");
        print_time(&tm);
        printf("Done.\n");
        }
      }
    term_port();
    }


/**********************************************************************
 * input_time()  --  prompt for the time and date and place in the    *
 *                   given nice_time structure                        *
 *                   returns 0 normally, -1 if error                  *
 **********************************************************************/

int input_time(tm)
    struct nice_time *tm;
    {
    int get_int();

    printf("If you goof or change your mind and don't want to set the\n");
    printf("clocks, just press return\n");
    printf("\n");
    if ((tm->year        = get_int("Year, e.g. 1986")) == -1)
      return(-1);
    if ((tm->month       = get_int("Month (1..12)"  )) == -1)
      return(-1);
    if ((tm->day         = get_int("Day (1..31)"    )) == -1)
      return(-1);
    if ((tm->day_of_week = get_int("Day of Week (0=Sun, ..., 6=Sat)")) == -1)
      return(-1);
    if ((tm->hour        = get_int("Hour (0..23)"   )) == -1)
      return(-1);
    if ((tm->minute      = get_int("Minute (0..59)" )) == -1)
      return(-1);
    tm->second = 0;

    return(0);
    }


/**********************************************************************
 * get_int()  --  display the given prompt and read an integer,       *
 *               returning it or -1 if a blank line is input          *
 **********************************************************************/

int get_int(prompt)
    char *prompt;

    {
    char buf[80];
    char *cp;

    int atoi();
    void getline();

    printf("%s: ", prompt);
    fflush(stdout);
    getline(buf);
    for (cp=buf; *cp == ' '; ++cp)
      ;
    if (*cp == '\0')
      return(-1);
    else
      return(atoi(cp));
    }

/**********************************************************************
 * getline()  --  read a line from stdin                              *
 **********************************************************************/

void getline(buf)
    char *buf;
    {
    int ch;

    while ((ch = getchar()) != '\n'  && ch != EOF)
      *buf++ = ch;
    *buf = '\0';
    }


/**********************************************************************
 * print_time()  --  display the contents of a nice_time structure    *
 **********************************************************************/

void print_time(tm)
    struct nice_time *tm;
    {
    static char *mnames[12] =
      {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
       "Oct", "Nov", "Dec"};
    static char *dnames[7] =
      {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
       "Saturday"};

    printf("%-9s %2d-%s-%02d %2d:%02d:%02d",
      dnames[tm->day_of_week],
      tm->day,
      mnames[tm->month-1],
      tm->year % 100,
      (tm->hour==0) ? 12 : (tm->hour<=12) ? tm->hour :
                                        tm->hour-12,
       tm->minute,
       tm->second);

    if (tm->hour < 12)
      printf(" AM\n");
    else
      printf(" PM\n");
}

/**********************************************************************
 * nice_to_sec()  --  convert the given nice_time structure into      *
 *                    seconds since 00:00:00 January 1, 1978          *
 **********************************************************************/

long nice_to_sec(tm)
    struct nice_time *tm;
    {
    long days_since_base;
    static int mdays[] =
      {
      0,
      0,
      31,
      31+28,
      31+28+31,
      31+28+31+30,
      31+28+31+30+31,
      31+28+31+30+31+30,
      31+28+31+30+31+30+31,
      31+28+31+30+31+30+31+31,
      31+28+31+30+31+30+31+31+30,
      31+28+31+30+31+30+31+31+30+31,
      31+28+31+30+31+30+31+31+30+31+30
      };
    static int leapmdays[] =
      {
      0,
      0,
      31,
      31+29,
      31+29+31,
      31+29+31+30,
      31+29+31+30+31,
      31+29+31+30+31+30,
      31+29+31+30+31+30+31,
      31+29+31+30+31+30+31+31,
      31+29+31+30+31+30+31+31+30,
      31+29+31+30+31+30+31+31+30+31,
      31+29+31+30+31+30+31+31+30+31+30
      };

      /* first compute number of days since January 1, 1978 */
    days_since_base = (tm->year-1978) * 365L + (tm->year-1977)/4;
    if (tm->year%4 == 0)
      days_since_base += leapmdays[tm->month];
    else
      days_since_base += mdays[tm->month];
    days_since_base += tm->day-1;

      /* now the rest is easy */
    return(days_since_base * (60L*60L*24L) + tm->hour * (60L*60L) +
           tm->minute * (60L) + tm->second);
    }


/**********************************************************************
 * set_sys_time()  --  set the system time                            *
 *                     Returns 0 normally, and -1 if an error occurs  *
 **********************************************************************/

int set_sys_time(secs)
    long secs;
    {
    struct timerequest tr;

    if (OpenDevice (TIMERNAME, UNIT_VBLANK, &tr, 0L) != 0L)
      {
      printf("Clock error: can't open timer device\n");
      return(-1);
      }
    tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    tr.tr_node.io_Message.mn_Node.ln_Pri = 0L;
    tr.tr_node.io_Message.mn_Node.ln_Name = NULL;
    tr.tr_node.io_Message.mn_ReplyPort = NULL;
    tr.tr_node.io_Command = TR_SETSYSTIME;
    tr.tr_time.tv_secs = secs;
    tr.tr_time.tv_micro = 0L;

    if (DoIO (&tr) != 0)
      {
      printf("Clock error: can't talk to timer device\n");
      CloseDevice(&tr);
      return(-1);
      }
    CloseDevice (&tr);
    return(0);
    }

/**********************************************************************
 * check_time()  --  see if the given nice_time structure contains    *
 *                   reasonable values; return 0 if so, -1 otherwise  *
 **********************************************************************/

int check_time(tm)
    struct nice_time *tm;
    {
#define WITHIN(val, lower, upper)  ((lower<=val) & (val<=upper))

    if (WITHIN(tm->year,        1978, 1999) &&
        WITHIN(tm->month,       1,      12) &&
        WITHIN(tm->day,         1,      31) &&
        WITHIN(tm->day_of_week, 0,       6) &&
        WITHIN(tm->hour,        0,      23) &&
        WITHIN(tm->minute,      0,      59) &&
        WITHIN(tm->second,      0,      59))
      return(0);
    else
      return(-1);
    }



/*
 * Variables for the clock routines
 */

    struct Library *PotgoBase;  /* points to Potgo Resource */
    long potgo_bits;            /* mask indicating which bits of the */
                                /* potgo resource we have allocated */
    int held;                   /* 1 if the clock is currently stopped */

/**********************************************************************
 * init_port  --  initialize the joystick port (right game port) so   *
 *                that we can talk to the clock                       *
 *                Returns 0 normally, and -1 if an error occurs       *
 **********************************************************************/

int init_port()
    {
    void pause();

      /* open the Potgo resource so we can allocate ourselves the port */
    PotgoBase = (struct Library *)OpenResource(POTGONAME);
    if (PotgoBase == NULL)
      {
      printf("Clock error: can't open Potgo resource\n");
      return(-1);
      }

      /* ask for the output bits of the right game port */
    potgo_bits = AllocPotBits(0xF000L);
      /* see if we got all the bits we asked for */
    if (!((potgo_bits & 0x4000L) && (potgo_bits & 0x1000L)))
      {
      printf("Clock error: can't allocate gameport bits\n");
      return(-1);
      }

      /* enable pins 5 and 9 for output, setting them both to 1 */
    WritePotgo(0xF000L, 0xF000L);

    pause();    /* wait for lines to settle */
    held = 0;   /* indicate clock not being held */

    return(0);
    }


/**********************************************************************
 * term_port()  --  release the game port                             *
 **********************************************************************/

void term_port()
    {
      /* set pins 5 and 9 back to input mode */
    WritePotgo(0x0000L, 0xF000L);
    FreePotBits(potgo_bits);
      /* I would think we should close the resource here, but */
      /* CloseResource() doesn't seem to exist                */
    }


/**********************************************************************
 * set_sclk()  --  set the "SCLK" line high, by bringing pin 5 of the *
 *                 game port low                                      *
 **********************************************************************/

void set_sclk()
    {
    void pause();
    WritePotgo(0x0000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * clear_sclk()  --  set the "SCLK" line low, by bringing pin 5 of    *
 *                   the game port high                               *
 **********************************************************************/

void clear_sclk()
    {
    void pause();
    WritePotgo(0x1000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * set_sdata()  --  set the "SDATA" line high, by bringing pin 9 of   *
 *                  the game port low                                 *
 **********************************************************************/

void set_sdata()
    {
    void pause();
    WritePotgo(0x0000L, 0x4000L);
    pause();
    }

/**********************************************************************
 * clear_sdata()  --  set the "SDATA" line low, by bringing pin 9 of  *
 *                    the game port high                              *
 **********************************************************************/

void clear_sdata()
    {
    void pause();
    WritePotgo(0x4000L, 0x4000L);
    pause();
    }


/**********************************************************************
 * pause()  --  delay for enough time for the gameport output lines   *
 *              to settle.  The RKM gives this time as 300 micro-     *
 *              seconds, so to be on the safe side this routine       *
 *              pauses for about 500 microseconds.                    *
 *              This routine depends on the CPU being a 68000 running *
 *              at 7.16 Mhz                                           *
 **********************************************************************/

void pause()
    {
    int i;

    for (i=1; i<= 70; ++i)
      ;
    }

/***********************************************************************
 * send()  --  send the given thirteen bits of data to the clock       *
 *             curcuit.  The bits in the data word are                 *
 *                                                                     *
 *   A3   A2   A1   A0   RD   WBE  HOLD WR   D3   D2   D1   D0=S1 S2   *
 *   12   11   10    9    8    7    6    5    4    3    2    1    0    *
 *  (MSB)                                                       (LSB)  *
 ***********************************************************************/

void send(d)
    int d;
    {
    int i;

      /* shift the bits into the shift register */
    for (i=1; i<=13; ++i)
      {
      if ((d & 1) == 1)
        set_sdata();
      else
        clear_sdata();
      set_sclk();               /* strobe the bit in */
      if (i<13)
        clear_sclk();
      d >>= 1;
      }
    clear_sdata();
    set_sdata();                /* latch the shift register contents */
    clear_sclk();
    }

/**********************************************************************
 * write_reg()  --  write the given value into the given register     *
 **********************************************************************/

void write_reg(r,v)
    int r,v;
    {
    void send();

    send(held<<6);
    send((r<<9) | 0x080 | (held<<6) | (v<<1));
    send((r<<9) | 0x0a0 | (held<<6) | (v<<1));
    send((r<<9) | 0x080 | (held<<6) | (v<<1));
    send(held<<6);
    }

/**********************************************************************
 * hold()  --  stop the clock, and set the held flag so that the      *
 *             clock will remain held while register reads or writes  *
 *             are done                                               *
 **********************************************************************/

void hold()
    {
    held = 1;
    send(0x040);
    }

/**********************************************************************
 * no_hold()  -- restart the clock if it was held                     *
 **********************************************************************/

void no_hold()
    {
    held = 0;
    send(0x000);
    }

/**********************************************************************
 * set_time()  --  set the clock                                      *
 **********************************************************************/

void set_time(tm)
    struct nice_time *tm;
    {
    void hold(), nohold(), write_reg();
    int leap_year, pm;

    leap_year = (tm->year%4 == 0);
    pm = (tm->hour >= 12);

    hold();

    write_reg(12, (tm->year%100)/10);
    write_reg(11, tm->year%10);
    write_reg(10, tm->month/10);
    write_reg(9,  tm->month%10);
    write_reg(8,  (tm->day/10) | (leap_year<<2));
    write_reg(7,  tm->day%10);
    write_reg(6,  tm->day_of_week);
    write_reg(5,  (tm->hour/10) | (pm<<2) | 8);
    write_reg(4,  tm->hour%10);
    write_reg(3,  tm->minute/10);
    write_reg(2,  tm->minute%10);
    write_reg(1,  tm->second/10);
    write_reg(0,  tm->second%10);

    no_hold();

    }

SHAR_EOF
if test 14906 -ne "`wc -c < 'clockwr.c'`"
then
	echo shar: error transmitting "'clockwr.c'" '(should have been 14906 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'clockrd.c'" '(13617 characters)'
if test -f 'clockrd.c'
then
	echo shar: will not over-write existing file "'clockrd.c'"
else
cat << \SHAR_EOF > 'clockrd.c'
/**********************************************************************
 * clock.c  --  read the time from the hardware clock, and set the    *
 *              software clock to match.  If the -d option is given,  *
 *              the time is displayed as well.                        *
 **********************************************************************/

#include <stdio.h>
#include <exec/types.h>
#include <hardware/cia.h>
#include <resources/potgo.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <devices/timer.h>
#include <functions.h>

#include "nice_time.h"

/**********************************************************************
 * main program                                                       *
 **********************************************************************/

main(argc, argv)
    int argc;
    char *argv[];
    {
    int dflag;
    struct nice_time tm;
    long seconds;

    void term_port(), get_time(), print_time();
    int  init_port(), set_sys_time(), check_time();
    long nice_to_sec();

    dflag = 0;
    if (argc == 2 && strcmp(argv[1], "-d") == 0)
      dflag = 1;

    if (init_port() != 0)
      exit(1);

    get_time(&tm);                      /* read the hardware clock */
    term_port();
    if (check_time(&tm) == -1)          /* see if values reasonable */
      {                                 /* (if not, the clock is not */
                                        /*  set or not there)        */
      printf("Clock error: clock contents not valid\n");
      exit(1);
      }
    if (dflag)
      print_time(&tm);
    seconds = nice_to_sec(&tm);         /* convert time to seconds since */
                                        /*  the base date                */
    (void)set_sys_time(seconds);        /* set the software clock        */
    }


/*
 * Variables for the clock routine
 */

    struct Library *PotgoBase;  /* points to Potgo Resource */
    long potgo_bits;            /* mask indicating which bits of the */
                                /* potgo resource we have allocated */
    int held;                   /* 1 if the clock is currently stopped */

/**********************************************************************
 * init_port  --  initialize the joystick port (right game port) so   *
 *                that we can talk to the clock                       *
 *                Returns 0 normally, and -1 if an error occurs       *
 **********************************************************************/

int init_port()
    {
    void pause();

      /* open the Potgo resource so we can allocate ourselves the port */
    PotgoBase = (struct Library *)OpenResource(POTGONAME);
    if (PotgoBase == NULL)
      {
      printf("Clock error: can't open Potgo resource\n");
      return(-1);
      }

      /* ask for the output bits of the right game port */
    potgo_bits = AllocPotBits(0xF000L);
      /* see if we got all the bits we asked for */
    if (!((potgo_bits & 0x4000L) && (potgo_bits & 0x1000L)))
      {
      printf("Clock error: can't allocate gameport bits\n");
      return(-1);
      }

      /* enable pins 5 and 9 for output, setting them both to 1 */
    WritePotgo(0xF000L, 0xF000L);

    pause();    /* wait for lines to settle */
    held = 0;   /* indicate clock not being held */

    return(0);
    }


/**********************************************************************
 * term_port()  --  release the game port                             *
 **********************************************************************/

void term_port()
    {
      /* set pins 5 and 9 back to input mode */
    WritePotgo(0x0000L, 0xF000L);
    FreePotBits(potgo_bits);
      /* I would think we should close the resource here, but */
      /* CloseResource() doesn't seem to exist                */
    }


/**********************************************************************
 * set_sclk()  --  set the "SCLK" line high, by bringing pin 5 of the *
 *                 game port low                                      *
 **********************************************************************/

void set_sclk()
    {
    void pause();
    WritePotgo(0x0000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * clear_sclk()  --  set the "SCLK" line low, by bringing pin 5 of    *
 *                   the game port high                               *
 **********************************************************************/

void clear_sclk()
    {
    void pause();
    WritePotgo(0x1000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * set_sdata()  --  set the "SDATA" line high, by bringing pin 9 of   *
 *                  the game port low                                 *
 **********************************************************************/

void set_sdata()
    {
    void pause();
    WritePotgo(0x0000L, 0x4000L);
    pause();
    }

/**********************************************************************
 * clear_sdata()  --  set the "SDATA" line low, by bringing pin 9 of  *
 *                    the game port high                              *
 **********************************************************************/

void clear_sdata()
    {
    void pause();
    WritePotgo(0x4000L, 0x4000L);
    pause();
    }


/**********************************************************************
 * pause()  --  delay for enough time for the gameport output lines   *
 *              to settle.  The RKM gives this time as 300 micro-     *
 *              seconds, so to be on the safe side this routine       *
 *              pauses for about 500 microseconds.                    *
 *              This routine depends on the CPU being a 68000 running *
 *              at 7.16 Mhz                                           *
 **********************************************************************/

void pause()
    {
    int i;

    for (i=1; i<= 70; ++i)
      ;
    }

/***********************************************************************
 * send()  --  send the given thirteen bits of data to the clock       *
 *             curcuit.  The bits in the data word are                 *
 *                                                                     *
 *   A3   A2   A1   A0   RD   WBE  HOLD WR   D3   D2   D1   D0=S1 S2   *
 *   12   11   10    9    8    7    6    5    4    3    2    1    0    *
 *  (MSB)                                                       (LSB)  *
 ***********************************************************************/

void send(d)
    int d;
    {
    int i;

      /* shift the bits into the shift register */
    for (i=1; i<=13; ++i)
      {
      if ((d & 1) == 1)
        set_sdata();
      else
        clear_sdata();
      set_sclk();               /* strobe the bit in */
      if (i<13)
        clear_sclk();
      d >>= 1;
      }
    clear_sdata();
    set_sdata();                /* latch the shift register contents */
    clear_sclk();
    }

/**********************************************************************
 * read_reg()  --  read and return the contents of the given register *
 **********************************************************************/

int read_reg(r)
    int r;
    {
    int data, bit0, bit1, bit2, bit3, i;
    UWORD raw_data;
    void send(), set_sclk(), clear_sclk();

      /* macro to read the state of pin 6 of the port  */
      /* (ciaa is a macro defined in <hardware/cia.h>) */
#define READBIT ((ciaa.ciapra & CIAF_GAMEPORT1) != 0)

      /* send the read command */
    send((r<<9) | 0x10C | (held<<6));

        /* read the data */
                              bit0 = READBIT;
    set_sclk(); clear_sclk(); bit1 = READBIT;
    set_sclk(); clear_sclk(); bit3 = READBIT;
    set_sclk(); clear_sclk(); bit2 = READBIT;

    data = (bit0     ) |
           (bit1 << 1) |
           (bit2 << 2) |
           (bit3 << 3);

    return(data);
    }

/**********************************************************************
 * hold()  --  stop the clock, and set the held flag so that the      *
 *             clock will remain held while register reads or writes  *
 *             are done                                               *
 **********************************************************************/

void hold()
    {
    held = 1;
    send(0x040);
    }

/**********************************************************************
 * no_hold()  -- restart the clock if it was held                     *
 **********************************************************************/

void no_hold()
    {
    held = 0;
    send(0x000);
    }

/**********************************************************************
 * get_time()  --  read the time from the clock and place it in a     *
 *                 nice_time structure.                               *
 **********************************************************************/

void get_time(tm)
    struct nice_time *tm;
    {
    void hold(), nohold();
    int  read_reg();

    hold();

    tm->year = 1900 + 10 * read_reg(12) + read_reg(11);
    tm->month = 10 * (read_reg(10)&1) + read_reg(9);
    tm->day  = 10 * (read_reg(8)&3) + read_reg(7);
    tm->day_of_week = read_reg(6);
    tm->hour = 10 * (read_reg(5)&3) + read_reg(4);
    tm->minute = 10 * (read_reg(3)&7) + read_reg(2);
    tm->second = 10 * (read_reg(1)&7) + read_reg(0);

    no_hold();

    }

/**********************************************************************
 * print_time()  --  display the contents of a nice_time structure    *
 **********************************************************************/

void print_time(tm)
    struct nice_time *tm;
    {
    static char *mnames[12] =
      {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
       "Oct", "Nov", "Dec"};
    static char *dnames[7] =
      {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
       "Saturday"};

    printf("%-9s %2d-%s-%02d %2d:%02d:%02d",
      dnames[tm->day_of_week],
      tm->day,
      mnames[tm->month-1],
      tm->year % 100,
      (tm->hour==0) ? 12 : (tm->hour<=12) ? tm->hour :
                                        tm->hour-12,
       tm->minute,
       tm->second);

    if (tm->hour < 12)
      printf(" AM\n");
    else
      printf(" PM\n");
}

/**********************************************************************
 * nice_to_sec()  --  convert the given nice_time structure into      *
 *                    seconds since 00:00:00 January 1, 1978          *
 **********************************************************************/

long nice_to_sec(tm)
    struct nice_time *tm;
    {
    long days_since_base;
    static int mdays[] =
      {
      0,
      0,
      31,
      31+28,
      31+28+31,
      31+28+31+30,
      31+28+31+30+31,
      31+28+31+30+31+30,
      31+28+31+30+31+30+31,
      31+28+31+30+31+30+31+31,
      31+28+31+30+31+30+31+31+30,
      31+28+31+30+31+30+31+31+30+31,
      31+28+31+30+31+30+31+31+30+31+30
      };
    static int leapmdays[] =
      {
      0,
      0,
      31,
      31+29,
      31+29+31,
      31+29+31+30,
      31+29+31+30+31,
      31+29+31+30+31+30,
      31+29+31+30+31+30+31,
      31+29+31+30+31+30+31+31,
      31+29+31+30+31+30+31+31+30,
      31+29+31+30+31+30+31+31+30+31,
      31+29+31+30+31+30+31+31+30+31+30
      };

      /* first compute number of days since January 1, 1978 */
    days_since_base = (tm->year-1978) * 365L + (tm->year-1977)/4;
    if (tm->year%4 == 0)
      days_since_base += leapmdays[tm->month];
    else
      days_since_base += mdays[tm->month];
    days_since_base += tm->day-1;

      /* now the rest is easy */
    return(days_since_base * (60L*60L*24L) + tm->hour * (60L*60L) +
           tm->minute * (60L) + tm->second);
    }


/**********************************************************************
 * set_sys_time()  --  set the system time                            *
 *                     Returns 0 normally, and -1 if an error occurs  *
 **********************************************************************/

int set_sys_time(secs)
    long secs;
    {
    struct timerequest tr;

    if (OpenDevice (TIMERNAME, UNIT_VBLANK, &tr, 0L) != 0L)
      {
      printf("Clock error: can't open timer device\n");
      return(-1);
      }
    tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    tr.tr_node.io_Message.mn_Node.ln_Pri = 0L;
    tr.tr_node.io_Message.mn_Node.ln_Name = NULL;
    tr.tr_node.io_Message.mn_ReplyPort = NULL;
    tr.tr_node.io_Command = TR_SETSYSTIME;
    tr.tr_time.tv_secs = secs;
    tr.tr_time.tv_micro = 0L;

    if (DoIO (&tr) != 0)
      {
      printf("Clock error: can't talk to timer device\n");
      CloseDevice(&tr);
      return(-1);
      }
    CloseDevice (&tr);
    return(0);
    }

/**********************************************************************
 * check_time()  --  see if the given nice_time structure contains    *
 *                   reasonable values; return 0 if so, -1 otherwise  *
 **********************************************************************/

int check_time(tm)
    struct nice_time *tm;
    {
#define WITHIN(val, lower, upper)  ((lower<=val) & (val<=upper))

    if (WITHIN(tm->year,        1978, 1999) &&
        WITHIN(tm->month,       1,      12) &&
        WITHIN(tm->day,         1,      31) &&
        WITHIN(tm->day_of_week, 0,       6) &&
        WITHIN(tm->hour,        0,      23) &&
        WITHIN(tm->minute,      0,      59) &&
        WITHIN(tm->second,      0,      59))
      return(0);
    else
      return(-1);
    }

SHAR_EOF
if test 13617 -ne "`wc -c < 'clockrd.c'`"
then
	echo shar: error transmitting "'clockrd.c'" '(should have been 13617 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'clocktest.c'" '(13473 characters)'
if test -f 'clocktest.c'
then
	echo shar: will not over-write existing file "'clocktest.c'"
else
cat << \SHAR_EOF > 'clocktest.c'
#include <stdio.h>
#include <exec/types.h>
#include <hardware/custom.h>
#include <hardware/cia.h>
#include <resources/potgo.h>
#include <functions.h>

#include "nice_time.h"

/**********************************************************************
 * Clock hardware testing program                                     *
 *   accepts the commands                                             *
 *     sd         set the SDATA line high                             *
 *     cd         set the SDATA line low                              *
 *     sc         set the SCLK  line high                             *
 *     cc         set the SCLK  line low                              *
 *     send num   send and latch the given 13-bit value (num in hex)  *
 *     read reg   read the indicated register (reg in hex)            *
 *     write r v  write v into register r (r and v in hex)            *
 *     hold       stop the clock                                      *
 *     nohold     restart the clock                                   *
 *     get        get and display the time                            *
 *     set y m d dw h m s      set the time                           *
 *       e.g.  set 1987 3 7 6 20 43 00  for Saturday March y, 1987    *
 *                                          8:43 PM                   *
 *     quit       exit the program                                    *
 **********************************************************************/

main()
    {
    char buf[800];
    int  d, r;

    void getline(), term_port(), set_sclk(), set_sdata(),
         clear_sclk(), clear_sdata(), send(), write_reg(),
         hold(), no_hold(), get_time(), set_time(), print_time();
    int  init_port(), read_reg();

    printf("Clock testing program\n");
    if (init_port() != 0)
      exit(1);
    for (;;)
      {
      printf("Command: ");
      fflush(stdout);
      getline(buf);
      if (strcmp(buf, "quit") == 0)
        break;
      else if (strcmp(buf, "sd") == 0)
        set_sdata();
      else if (strcmp(buf, "cd") == 0)
        clear_sdata();
      else if (strcmp(buf, "sc") == 0)
        set_sclk();
      else if (strcmp(buf, "cc") == 0)
        clear_sclk();
      else if (strncmp(buf, "send", 4) == 0)
        {
        sscanf(buf+4, "%x", &d);
        send(d);
        }
      else if (strncmp(buf, "read", 4) == 0)
        {
        sscanf(buf+4, "%x", &r);
        d = read_reg(r);
        printf("Register %x contains %x\n", r, d);
        }
      else if (strncmp(buf, "write", 5) == 0)
        {
        sscanf(buf+5, "%x %x", &r, &d);
        write_reg(r,d);
        }
      else if (strcmp(buf, "hold") == 0)
        hold();
      else if (strcmp(buf, "nohold") == 0)
        no_hold();
      else if (strcmp(buf, "get") == 0)
        {
        struct nice_time tm;
        get_time(&tm);
        print_time(&tm);
        }
      else if (strncmp(buf, "set", 3) == 0)
        {
        struct nice_time tm;
        sscanf(buf+3, "%d %d %d %d %d %d %d",
          &tm.year, &tm.month, &tm.day, &tm.day_of_week,
          &tm.hour, &tm.minute, &tm.second);
        set_time(&tm);
        }
      else
        printf("Unknown comand\n");
      }
    term_port();
    }

/**********************************************************************
 * getline()  --  read a line from stdin                              *
 **********************************************************************/

void getline(buf)
    char *buf;
    {
    int ch;

    while ((ch = getchar()) != '\n'  && ch != EOF)
      *buf++ = ch;
    *buf = '\0';
    }


/*
 * Variables for the clock routines
 */

    struct Library *PotgoBase;  /* points to Potgo Resource */
    long potgo_bits;            /* mask indicating which bits of the */
                                /* potgo resource we have allocated */
    int held;                   /* 1 if the clock is currently stopped */

/**********************************************************************
 * init_port  --  initialize the joystick port (right game port) so   *
 *                that we can talk to the clock                       *
 *                Returns 0 normally, and -1 if an error occurs       *
 **********************************************************************/

int init_port()
    {
    void pause();

      /* open the Potgo resource so we can allocate ourselves the port */
    PotgoBase = (struct Library *)OpenResource(POTGONAME);
    if (PotgoBase == NULL)
      {
      printf("Clock error: can't open Potgo resource\n");
      return(-1);
      }

      /* ask for the output bits of the right game port */
    potgo_bits = AllocPotBits(0xF000L);
      /* see if we got all the bits we asked for */
    if (!((potgo_bits & 0x4000L) && (potgo_bits & 0x1000L)))
      {
      printf("Clock error: can't allocate gameport bits\n");
      return(-1);
      }

      /* enable pins 5 and 9 for output, setting them both to 1 */
    WritePotgo(0xF000L, 0xF000L);

    pause();    /* wait for lines to settle */
    held = 0;   /* indicate clock not being held */

    return(0);
    }


/**********************************************************************
 * term_port()  --  release the game port                             *
 **********************************************************************/

void term_port()
    {
      /* set pins 5 and 9 back to input mode */
    WritePotgo(0x0000L, 0xF000L);
    FreePotBits(potgo_bits);
      /* I would think we should close the resource here, but */
      /* CloseResource() doesn't seem to exist                */
    }


/**********************************************************************
 * set_sclk()  --  set the "SCLK" line high, by bringing pin 5 of the *
 *                 game port low                                      *
 **********************************************************************/

void set_sclk()
    {
    void pause();
    WritePotgo(0x0000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * clear_sclk()  --  set the "SCLK" line low, by bringing pin 5 of    *
 *                   the game port high                               *
 **********************************************************************/

void clear_sclk()
    {
    void pause();
    WritePotgo(0x1000L, 0x1000L);
    pause();
    }

/**********************************************************************
 * set_sdata()  --  set the "SDATA" line high, by bringing pin 9 of   *
 *                  the game port low                                 *
 **********************************************************************/

void set_sdata()
    {
    void pause();
    WritePotgo(0x0000L, 0x4000L);
    pause();
    }

/**********************************************************************
 * clear_sdata()  --  set the "SDATA" line low, by bringing pin 9 of  *
 *                    the game port high                              *
 **********************************************************************/

void clear_sdata()
    {
    void pause();
    WritePotgo(0x4000L, 0x4000L);
    pause();
    }


/**********************************************************************
 * pause()  --  delay for enough time for the gameport output lines   *
 *              to settle.  The RKM gives this time as 300 micro-     *
 *              seconds, so to be on the safe side this routine       *
 *              pauses for about 500 microseconds.                    *
 *              This routine depends on the CPU being a 68000 running *
 *              at 7.16 Mhz                                           *
 **********************************************************************/

void pause()
    {
    int i;

    for (i=1; i<= 70; ++i)
      ;
    }

/***********************************************************************
 * send()  --  send the given thirteen bits of data to the clock       *
 *             circuit.  The bits in the data word are                 *
 *                                                                     *
 *   A3   A2   A1   A0   RD   WBE  HOLD WR   D3   D2   D1   D0=S1 S2   *
 *   12   11   10    9    8    7    6    5    4    3    2    1    0    *
 *  (MSB)                                                       (LSB)  *
 ***********************************************************************/

void send(d)
    int d;
    {
    int i;

      /* shift the bits into the shift register */
    for (i=1; i<=13; ++i)
      {
      if ((d & 1) == 1)
        set_sdata();
      else
        clear_sdata();
      set_sclk();               /* strobe the bit in */
      if (i<13)
        clear_sclk();
      d >>= 1;
      }
    clear_sdata();
    set_sdata();                /* latch the shift register contents */
    clear_sclk();
    }

/**********************************************************************
 * read_reg()  --  read and return the contents of the given register *
 **********************************************************************/

int read_reg(r)
    int r;
    {
    int data, bit0, bit1, bit2, bit3, i;
    UWORD raw_data;
    void send(), set_sclk(), clear_sclk();

      /* macro to read the state of pin 6 of the port  */
      /* (ciaa ia a macro defined in <hardware.cia.h>) */
#define READBIT ((ciaa.ciapra & CIAF_GAMEPORT1) != 0)

      /* send the read command */
    send((r<<9) | 0x10C | (held<<6));

        /* read the data */
                              bit0 = READBIT;
    set_sclk(); clear_sclk(); bit1 = READBIT;
    set_sclk(); clear_sclk(); bit3 = READBIT;
    set_sclk(); clear_sclk(); bit2 = READBIT;

    data = (bit0     ) |
           (bit1 << 1) |
           (bit2 << 2) |
           (bit3 << 3);

    return(data);
    }

/**********************************************************************
 * write_reg()  --  write the given value into the given register     *
 **********************************************************************/

void write_reg(r,v)
    int r,v;
    {
    void send();

    send(held<<6);
    send((r<<9) | 0x080 | (held<<6) | (v<<1));
    send((r<<9) | 0x0a0 | (held<<6) | (v<<1));
    send((r<<9) | 0x080 | (held<<6) | (v<<1));
    send(held<<6);
    }

/**********************************************************************
 * hold()  --  stop the clock, and set the held flag so that the      *
 *             clock will remain held while register reads or writes  *
 *             are done                                               *
 **********************************************************************/

void hold()
    {
    held = 1;
    send(0x040);
    }

/**********************************************************************
 * no_hold()  -- restart the clock if it was held                     *
 **********************************************************************/

void no_hold()
    {
    held = 0;
    send(0x000);
    }

/**********************************************************************
 * get_time()  --  read the time from the clock and place it in a     *
 *                 nice_time structure.                               *
 **********************************************************************/

void get_time(tm)
    struct nice_time *tm;
    {
    void hold(), nohold();
    int  read_reg();

    hold();

    tm->year = 1900 + 10 * read_reg(12) + read_reg(11);
    tm->month = 10 * (read_reg(10)&1) + read_reg(9);
    tm->day  = 10 * (read_reg(8)&3) + read_reg(7);
    tm->day_of_week = read_reg(6);
    tm->hour = 10 * (read_reg(5)&3) + read_reg(4);
    tm->minute = 10 * (read_reg(3)&7) + read_reg(2);
    tm->second = 10 * (read_reg(1)&7) + read_reg(0);

    no_hold();

    }

/**********************************************************************
 * print_time()  --  display the contents of a nice_time structure    *
 **********************************************************************/

void print_time(tm)
    struct nice_time *tm;
    {
    static char *mnames[12] =
      {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
       "Oct", "Nov", "Dec"};
    static char *dnames[7] =
      {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
       "Saturday"};

    printf("%-9s %2d-%s-%02d %2d:%02d:%02d",
      dnames[tm->day_of_week],
      tm->day,
      mnames[tm->month-1],
      tm->year % 100,
      (tm->hour==0) ? 12 : (tm->hour<=12) ? tm->hour :
                                        tm->hour-12,
       tm->minute,
       tm->second);

    if (tm->hour < 12)
      printf(" AM\n");
    else
      printf(" PM\n");
}


/**********************************************************************
 * set_time()  --  set the clock                                      *
 **********************************************************************/

void set_time(tm)
    struct nice_time *tm;
    {
    void hold(), nohold(), write_reg();
    int leap_year, pm;

    leap_year = (tm->year%4 == 0);
    pm = (tm->hour >= 12);

    hold();

    write_reg(12, (tm->year%100)/10);
    write_reg(11, tm->year%10);
    write_reg(10, tm->month/10);
    write_reg(9,  tm->month%10);
    write_reg(8,  (tm->day/10) | (leap_year<<2));
    write_reg(7,  tm->day%10);
    write_reg(6,  tm->day_of_week);
    write_reg(5,  (tm->hour/10) | (pm<<2) | 8);
    write_reg(4,  tm->hour%10);
    write_reg(3,  tm->minute/10);
    write_reg(2,  tm->minute%10);
    write_reg(1,  tm->second/10);
    write_reg(0,  tm->second%10);

    no_hold();

    }

SHAR_EOF
if test 13473 -ne "`wc -c < 'clocktest.c'`"
then
	echo shar: error transmitting "'clocktest.c'" '(should have been 13473 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'nice_time.h'" '(409 characters)'
if test -f 'nice_time.h'
then
	echo shar: will not over-write existing file "'nice_time.h'"
else
cat << \SHAR_EOF > 'nice_time.h'
/*
 * nice_time.h  -- a structure containing the time and date in a
 *                 convenient form
 */

struct nice_time
  {
  int year;             /* e.g. 1986 */
  int month;            /* 1...12 */
  int day;              /* 1...31  */
  int day_of_week;      /* 0(Sun)...6(Sat) */
  int hour;             /* 0...23 */
  int minute;           /* 0...59 */
  int second;           /* 0...59 */
  } ;


SHAR_EOF
if test 409 -ne "`wc -c < 'nice_time.h'`"
then
	echo shar: error transmitting "'nice_time.h'" '(should have been 409 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0