/*

a radian is the measure of an angle with its with vertex at the
center of a circle whose intercepted arc on the circle is equal
in length to the radius of the circle...

 - allyn j. washington

*/

/*

Ben.C (C) Copyright Bill Buckels 1989-2008.
All rights reserved.

Written by   : Bill Buckels
Email        : bbuckels@mts.net

Date Written : May 2008

Environment  : Apple33 Manx Aztec C65 Version 3.2b
Windows XP Cross-development environment for DOS33

I have made absolutely no attempt to make this program
compatible with any C compiler outside the Apple33
environment stated above.

Purpose      : Children's Activity

Description
-----------

A Clock Program for the Apple II based on the March 1991
IBM-PC Version which was written in Mix Power C which
this is none of the above except for the part about
the clock program..

Although this is a complete rewrite, and basic redesign,
it was only finished to a point that I consider
reasonably functional. Please do not hold this source up
as an example of the way we should work, because there
is much room left to finish this program off if life was
endless, although functionally everything is working as
far as I know.

The important thing is to get it out there for the
kiddies to use. That includes the older kiddies as well,
meaning the rest of us.

Please refer to the ReadMe.txt for Little Ben for a
complete description and user documentation.

Licence Agreement
-----------------

All my work is copyrighted and belongs to me. This
program is not derived from anything by anyone and is my
own work in its entirety.

I herewith grant you a non-exclusive and conditional
licence to use this source code and the output files it
produces for whatever use you deem fit, provided you do
not take credit for my work, and that you leave my
copyright notices intact in all of it.

If you augment or otherwise use my work you must always
also include your own personal copyright notice but it
may never be a GNU public licence or anything else that
resembles fascism or totalitarianism and world-domination
or a commercial or educational licence either. You can
use my stuff commercially or for GNU with my conditions
intact if they let you (they should since copyright is
for authors and the public and I belong to both groups)
but you must never copyright my work with any company
copyright whatsoever; just your own personal copyright
like mine and leave mine in place. That is the way
copyright is intended to work and that is the way that it
will work with my stuff unless I selectively decide
otherwise.

In addition you must agree that I am not liable in any
way shape or form for any damage from the use of any of
this in any way whatsoever.

If you do not agree with all of the aforementioned
conditions of use or if your use is not Fair then remove
all of this from your computer now.

Bill Buckels
bbuckels@mts.net
May 2008

*/


#include <console.h>

#define ENGLISH 0
#define FRENCH 1

/* language flag for bilingual play */
int language = ENGLISH;

char *ekeys = "Most Keys and Arrows Hot-Esc Exits";
char *fkeys = "Appuyez sur les fleches pour jouer";

char *etitle = "Little Ben(C)1991-2008 B.Buckels";
char *ftitle = "Chez l'horloger(C) par B.Buckels ";

char *eproduct = "Made   ";
char *fproduct = "Produit";
char *eproduct2 = "In";
char *fproduct2 = "du";

char *henglish[14] ={
     "Twelve",
     "One",
     "Two",
     "Three",
     "Four",
     "Five",
     "Six",
     "Seven",
     "Eight",
     "Nine",
     "Ten",
     "Eleven",
     "Twelve",
     "One"};

char *hfrench[14]={
     "douze",
     "une",
     "deux",
     "trois",
     "quatre",
     "cinq",
     "six",
     "sept",
     "huit",
     "neuf",
     "dix",
     "onze",
     "douze",
     "une" };


char *menglish[31]={
     " O\'clock",
     "One",
     "Two",
     "Three",
     "Four",
     "Five",
     "Six",
     "Seven",
     "Eight",
     "Nine",
     "Ten",
     "Eleven",
     "Twelve",
     "Thirteen",
     "Fourteen",
     "Quarter",
     "Sixteen",
     "Seventeen",
     "Eighteen",
     "Nineteen",
     "Twenty",
     "Twenty-One",
     "Twenty-Two",
     "Twenty-Three",
     "Twenty-Four",
     "Twenty-Five",
     "Twenty-Six",
     "Twenty-Seven",
     "Twenty-Eight",
     "Twenty-Nine",
     " Thirty"};

char *mfrench[31]={
     "heure",
     "une",
     "deux",
     "trois",
     "quatre",
     "cinq",
     "six",
     "sept",
     "huit",
     "neuf",
     "dix",
     "onze",
     "douze",
     "treize",
     "quatorze",
     "quart",
     "seize",
     "dix-sept",
     "dix-huit",
     "dix-neuf",
     "vingt",
     "vingt et un",
     "vingt-deux",
     "vingt-trois",
     "vingt-quatre",
     "vingt-cinq",
     "vingt-six",
     "vingt-sept",
     "vingt-huit",
     "vingt-neuf",
     "et demie"};



/* the following is used by the clock functions */

/* a table for sine cosine */
/* 184 bytes of data */
/* the divisor is 32767 (a signed int)   */

int sine_cosine[46][2]={
0,    32767,/* 0 degree offset*/
571,  32762,/* 1 degree offset*/
1143, 32747,/* 2 degree offset*/
1714, 32722,/* 3 degree offset*/
2285, 32687,/* 4 degree offset*/
2855, 32642,/* 5 degree offset*/
3425, 32587,/* 6 degree offset*/
3993, 32522,/* 7 degree offset*/
4560, 32448,/* 8 degree offset*/
5125, 32363,/* 9 degree offset*/
5689, 32269,/* 10 degree offset*/
6252, 32164,/* 11 degree offset*/
6812, 32050,/* 12 degree offset*/
7370, 31927,/* 13 degree offset*/
7927, 31793,/* 14 degree offset*/
8480, 31650,/* 15 degree offset*/
9031, 31497,/* 16 degree offset*/
9580, 31335,/* 17 degree offset*/
10125,31163,/* 18 degree offset*/
10667,30981,/* 19 degree offset*/
11206,30790,/* 20 degree offset*/
11742,30590,/* 21 degree offset*/
12274,30381,/* 22 degree offset*/
12803,30162,/* 23 degree offset*/
13327,29934,/* 24 degree offset*/
13847,29696,/* 25 degree offset*/
14364,29450,/* 26 degree offset*/
14875,29195,/* 27 degree offset*/
15383,28931,/* 28 degree offset*/
15885,28658,/* 29 degree offset*/
16383,28377,/* 30 degree offset*/
16876,28086,/* 31 degree offset*/
17363,27787,/* 32 degree offset*/
17846,27480,/* 33 degree offset*/
18323,27165,/* 34 degree offset*/
18794,26841,/* 35 degree offset*/
19259,26509,/* 36 degree offset*/
19719,26168,/* 37 degree offset*/
20173,25820,/* 38 degree offset*/
20620,25464,/* 39 degree offset*/
21062,25100,/* 40 degree offset*/
21497,24729,/* 41 degree offset*/
21925,24350,/* 42 degree offset*/
22347,23964,/* 43 degree offset*/
22761,23570,/* 44 degree offset*/
23169,23169};/*45 degree offset*/

/* circlepoints uses a partial quadrant
   and some pythagorean style math
   plus an aspect ratio adjustment to chord
   the intersection of a point on an arc
   in pixel units
   with the radius defined by baselength.

   the calculation is done in part using
   floating point to some degree for precsion.
*/


int circlepoints(x,y,baselength,fdegrees)
int *x, *y;
int baselength;
float fdegrees;
{
    float xtemp;
    float ytemp;
    int degrees = (int )fdegrees;
    float aspect_h, aspect_v;

    aspect_h = (float)1.16;
    aspect_v = (float)1;

    /*  starting at 12 O'clock    */
    /* use a switch for the break */
    switch(degrees)
    {

        default:

        /* within range */
        if(degrees<0 || degrees >359)degrees=0;

        /*  0-45  sin */
        if(degrees < 45)
        {
        xtemp = (float) sine_cosine[degrees][0];
        ytemp = (float) sine_cosine[degrees][1];
        ytemp = ytemp * -1;
        break;
        }

        /* 45-90  cos */
        if(degrees < 90)
        {
        xtemp = (float) sine_cosine[90-degrees][1];
        ytemp = (float) sine_cosine[90-degrees][0];
        ytemp = ytemp * -1;
        break;
        }

       /* 90 - 135 */
       if(degrees < 135)
       {
        xtemp = (float) sine_cosine[degrees-90][1];
        ytemp = (float) sine_cosine[degrees-90][0];
        break;
        }

        /* 135 - 180 */
       if(degrees< 180)
       {
        xtemp = (float) sine_cosine[180-degrees][0];
        ytemp = (float) sine_cosine[180-degrees][1];
        break;
        }

       /* 180 - 225 */
       if(degrees< 225)
       {
        xtemp = (float) sine_cosine[degrees-180][0];
        xtemp = xtemp * -1;
        ytemp = (float) sine_cosine[degrees-180][1];
        break;
        }

       /* 225 - 270 */
       if(degrees< 270)
       {
        xtemp = (float) sine_cosine[270-degrees][1];
        xtemp = xtemp * -1;
        ytemp = (float) sine_cosine[270-degrees][0];
        break;
        }

       /*  270 - 315 */
       if(degrees< 315)
       {
        xtemp = (float) sine_cosine[degrees-270][1];
        xtemp = xtemp * -1;
        ytemp = (float) sine_cosine[degrees-270][0];
        ytemp = ytemp * -1;
        break;
        }

        /* 315 - 360 */
        xtemp = (float) sine_cosine[360-degrees][0];
        xtemp = xtemp * -1;
        ytemp = (float) sine_cosine[360-degrees][1];
        ytemp = ytemp * -1;
    }

    ytemp = ytemp/32767;
    xtemp = xtemp/32767;

    xtemp *= (aspect_h*baselength);
    ytemp *= (aspect_v*baselength);

    *x += (int )xtemp;
    *y += (int )ytemp;
    return 0;

}

/* radius of the circle, radius for analog clock number datums */
/* note:

the number datums are centre-justified in pixel coordinates
and must be adjusted by half the height and width to
a cartesian type datum which is top-left justified to
print the numbers correctly around the periphery of the
circular clock face. the aspect ratio adjustment for the
screen is simplified using the circlepoints function
which provides the intersection of the arc decribed
by the letterbase, but the horizontal axis conversion of
pixels to bytes which completes the xterm datum calculation
for the font is somewhat of a nuisance to read.

a simpler method of designing an analog clock face would
have been to save a pre-formatted circle of the correct aspect
as an bitmapped image and then to place the other clock
elements like the numbers manually. but doing so would not
have been as elegant nor as much fun.

*/
int circlebase = 62, letterbase=72;

/* the radius of the extant for the minute marks
   on the analog face */
int markbase= 66;

/* length of the hands */
int hourbase=49, minutebase=55;
/* centre of the circle */
int rcx = 140, rcy = 98;

/* the calculation for the clock face buffer
   can be done by taking the circle base
   of 62 * 2 = 124 which is the vertical dimension
   and multiplying it by the screen aspect 1.14
   to get the horizontal dimension of 140.
   or 20 bytes per scanline.

   since our clock origin is in the
   centre of the screen it is easy to calculate
   our offsets etc. I just precalculate
   some of this stuff naturally but
   thought I'd best explain how
   I arrived at my figures */


#define FACE_WIDTH   18
#define FACE_HEIGHT  110
#define FACE_X 11
#define FACE_Y 43

/* clock face buffer */
char face[1980];
/* time string buffer */
char tbuf[40];

/* terse grammar */
char *heure = "une heure ";
char *heures = " heures ";
char *moins = "moins ";
char *etquart = "et quart";
char *etdemie = "et demie";
char *lequart = "le quart";
char *eafter = " after ";
char *eto = " to ";
char *eminute = " Minute";
char *eminutes = " Minutes";

/* note - avoiding the use of sprintf
   because of memory requirements.
   this program is very close to needing
   to be split into overlays and that
   is something for another day
   so using strcat and strcpy. */
int dobottom(hour,minute)
int hour, minute;
{

    char *hptr, *mptr;
    int next,prev;

    while(hour>11)hour-=12;

    /* a simple lexical grammar for building
       the plain language display string for the
       bottom of the screen

       this provides for idiomatic expressions
       related to time in both english and french

       this is a little easier to read than previous
       versions of Little Ben and perhaps now we can
       understand what is going on here without
       getting a headache.

       Note that the french expression always states
       the hours before the minutes. The english does
       not and this is simply a matter of syntax
       differences between the two languages. The french
       however uses articles for singular and plural
       so this requires additional conditions. In french
       the word "heure" is always stated and in english only
       O'Clock modifies the numeric hour and the noun "hour" is
       always dropped so we only need to worry about plurality
       in the noun "Minute(s)".

       Anyway, just thought I should mention all this.
       That's why the language condition is necessary.
       Thanks to Ken Penner of MCALC and Class Software for
       bringing this to my attention very early in the design
       of this program so I didn't look like a fool.

       */

    if(language==FRENCH)
    {
       hptr = (char *)&hfrench[hour][0];

       next = hour + 1;
       prev = 60 - minute;

       if (minute < 30)mptr = (char *)&mfrench[minute][0];
       else mptr = (char *)&mfrench[prev][0];

       switch(minute)
       {
          case 0 :  if(hour==1)
                       strcpy(tbuf,heure);
                    else {
                        strcpy(tbuf,hptr);
                        strcat(tbuf,heures);
                    }
                    break;
          case 15:  if(hour==1)
                       strcpy(tbuf,heure);
                    else {
                       strcpy(tbuf,hptr);
                       strcat(tbuf,heures);
                    }
                    strcat(tbuf,etquart);
                    break;
          case 30:  if(hour==1)
                       strcpy(tbuf,heure);
                    else {
                       strcpy(tbuf,hptr);
                       strcat(tbuf,heures);
                    }
                    strcat(tbuf,etdemie);
                    break;
          case 45:  if(hour==12||hour==0)
                       strcpy(tbuf,heure);
                    else {
                       hptr = (char *)&hfrench[next][0];
                       strcpy(tbuf,hptr);
                       strcat(tbuf,heures);
                    }
                    strcat(tbuf,moins);
                    strcat(tbuf,lequart);
                    break;
          default:
                   if(minute<30)
                   {
                       if(hour==1)
                           strcpy(tbuf,heure);
                       else {
                          strcpy(tbuf,hptr);
                          strcat(tbuf,heures);
                       }
                   }
                   else {
                       if(hour==12||hour==0)
                           strcpy(tbuf,heure);
                        else {
                          hptr = (char *)&hfrench[next][0];
                          strcpy(tbuf,hptr);
                          strcat(tbuf,heures);
                       }
                       strcat(tbuf,moins);
                   }
                   strcat(tbuf,mptr);
                   break;
          }
    }
    else
    {
        next = hour + 1;
        prev = 60 - minute;

        if (minute > 30) {
            hptr = (char *)&henglish[next][0];
            mptr = (char *)&menglish[prev][0];
        }
        else {
            hptr = (char *)&henglish[hour][0];
            mptr = (char *)&menglish[minute][0];
        }
        if (minute == 0 || minute == 30)strcpy(tbuf,hptr);
        else strcpy(tbuf,mptr);

        switch(minute)
        {
          case 0 :
          case 30:  strcat(tbuf,mptr);break;
          case 15:
                    strcat(tbuf,eafter);strcat(tbuf,hptr);break;
          case 45:
                    strcat(tbuf,eto);strcat(tbuf,hptr);break;
          default:
                   if(minute==1||minute==59)strcat(tbuf,eminute);
                   else strcat(tbuf,eminutes);

                   if(minute<30) strcat(tbuf,eafter);
                   else strcat(tbuf,eto);

                   strcat(tbuf,hptr);
                   break;
        }
    }


    /* return the string length */
    next = 0;
    for (;;) {
      if (tbuf[next] == 0)break;
      next++;
    }
    return next;

}

char *digit = "00:00";
int dx = 33,dy = 43;
int dodigital(hour,minute)
int hour, minute;
{

    while(hour>11)hour-=12;
    if (hour == 0)hour = 12;

    if (hour > 9)digit[0]= 49;
    else digit[0]=32;

    digit[1] = (hour % 10) + 48;
    digit[3] = (minute / 10) + 48;
    digit[4] = (minute % 10) + 48;

    return 0;

}


int dohands(hour,minute)
int hour, minute;
{
   int x, y;

   float hourdegrees;
   float minutedegrees;

   /* minutes is a simple calculation
      and convert directly to degrees */
   minutedegrees=(float)6*minute;

   while(hour>11)hour-=12;
   /* find the base position for the hours hand */
   hourdegrees= (float)30*hour;
   /* adjust hours hand for minutes between hours */
   hourdegrees+= (float).5*minute;

   /* draw the new hands */
   x=rcx;
   y=rcy;
   circlepoints(&x,&y,minutebase,minutedegrees);
   /* draw the minute hand */
   bdrw(rcx,rcy,x,y);

   x=rcx;
   y=rcy;
   circlepoints(&x,&y,hourbase,hourdegrees);
    /* draw the hour hand */
   drw(rcx,rcy,x,y);
   return 0;
}



main()
{

    int col, c, show;
    int hour = 12, minute = 0;

    /* set graphics mode and display title screen until a key is pressed */
    /* allow language selection at title screen as well as menu */
    setcrtmode(2);
    c = toupper(getch());
    fbox(0,0,39,191,0);

    if (c == 'F')language = FRENCH;

    /* draw the clock face. this is done only once */
    drawclock();

    /* display the initial screen then wait for keypresses */
    dodigital(hour,minute);
    plots(digit,dx,dy,1);
    col = dobottom(hour,minute);
    col = (39 - col)/2;
    plots(tbuf,col,182,1);
    dohands(hour,minute);
    dotitle();

    for (;;) {
        c = toupper(getch());
        if (c == ESCAPE) break;
        show = 1;
        switch(c) {

            case RTARROW:
               minute=(minute/5)*5; /* truncate to even intervals */
               minute+=5;
               if(minute>59) {
                  minute-=60;
                  hour++;
                }
                break;
            case LTARROW:
               minute=(minute/5)*5; /* truncate to even intervals */
               minute-=5;
               if(minute<0) {
                  minute+=60;
                  hour--;
                }
                break;
            case UPARROW:
                 hour --;
                 break;
            case DNARROW:
                 hour ++;
                 break;
            /* english */
            case 65:
            case 69:
                 show = 0;
                 if (language == ENGLISH) break;
                 show = 2;
                 language = ENGLISH;
                 break;
            case 70:
                 show = 0;
                 if (language == FRENCH) break;
                 show = 2;
                 language = FRENCH;
                 break;
            default:

                 minute++;
                 if(minute>59)
                 {
                    minute=0;
                    hour++;
                 }
                 break;
        }

        /* if we are changing languages, no need
           to redraw the clock. */

         if (show == 2)dotitle();

         if (show == 1) {
            if(hour>12)hour=1;
            if(hour<1)hour=12;

            /* print the new digital clock at the top of the screen */
            /* this erases the old one */
            dodigital(hour,minute);
            plots(digit,dx,dy,1);

         }

         /* erase the previous expression first */
         if (show != 0) fbox(1,182,38,189,0);

         if (show == 1) {
            /* erase the old hands */
            putimage((char *)&face[0],FACE_WIDTH,FACE_HEIGHT,FACE_X,FACE_Y,0);
            dohands(hour,minute);
         }

        if (show != 0) {
            /* print the time expression at the bottom of the screen */
            col = dobottom(hour,minute);
            col = (39 - col)/2;
            plots(tbuf,col,182,1);
        }

    }


setcrtmode(0);
scr_apple();
reboot();

}


/* helper functions */

/* sprintf is too bulky so for the small amount
   of text formatting needed here a local function will do */
int mknumber(num, ptr)
int num;
char *ptr;
{

    ptr[0] = 0;
    if (num > -1 && num < 100) {
        if (num < 10) {
           ptr[0] = num + 48;
           ptr[1] = 0;
        }
        else {
           ptr[0] = (num / 10) + 48;
           ptr[1] = (num % 10) + 48;
           ptr[2] = 0;
        }
    }
}


/* called initially and then
   throughout the program  when the language changes */
int dotitle()
{

    int idx;
    char *ptr, *ptr2, *ptr3, *ptr4;


    if (language == ENGLISH) {
        ptr  = (char *)&etitle[0];
        ptr2 = (char *)&ekeys[0];
        ptr3 = (char *)&eproduct[0];
        ptr4 = (char *)&eproduct2[0];
    }
    else {
        ptr  = (char *)&ftitle[0];
        ptr2 = (char *)&fkeys[0];
        ptr3 = (char *)&fproduct[0];
        ptr4 = (char *)&fproduct2[0];
    }


    idx = 0;
    for(;;) {
      if (ptr[idx] == 0)break;
      idx++;
    }
    idx = (39 - idx)/2;
    plots(ptr,idx,0,1);

    idx = 0;
    for(;;) {
      if (ptr2[idx] == 0)break;
      idx++;
    }
    idx = (39 - idx)/2;
    plots(ptr2,idx,8,1);

    plots(ptr3,1,dy,1);
    plots(ptr4,1, dy+10,1);
    plots("Canada",1, dy+20,1);

    return 0;

}

/* sets-up the clock face */
/* the title is drawn only once */
/* why not use a bitmap? well for one thing
   this is more fun to program and
   it is more fun to watch the clock being drawn...
   at least I think it is.
*/

drawclock()
{
    int idx, x, y, x1, y1, xfactor;
    float tempdegrees;
    char numbuf[3];

  /* do all the small marks */
   for(idx=0;idx<60;idx++)
   {
     /* graduations are every minute */
     tempdegrees= (float)6*idx;
     x = rcx;
     y = rcy;

     x1 = rcx;
     y1 = rcy;

     /* get starting point for small lines */
     circlepoints(&x,&y,circlebase,tempdegrees);

     if(idx%5==0) {
         circlepoints(&x1,&y1,markbase+4,tempdegrees);
         rdrw(x,y,x1,y1);
     }
     else {
         circlepoints(&x1,&y1,markbase,tempdegrees);
         bdrw(x,y,x1,y1);
     }
   }

    /* draw a box around the screen */
    y = rcy - letterbase - 8;
    rdrw(0,y,279,y);
    y++;
    bdrw(0,y,0,191);
    bdrw(279,y,279,191);
    rdrw(0,191,279,191);


    /* draw a box for the digital clock */
    y = dy - 4;
    x = (dx * 7) - 2;
    x1 = x+40;
    y1 = y+13;

    rdrw(x,y,x1,y);
    y++;
    rdrw(x,y,x,y1);
    rdrw(x1,y,x1,y1);
    rdrw(x,y1,x1,y1);

    /* draw the clock circle */
    circle(rcx,rcy,circlebase);
    /* draw a smaller circle in the center */
    circle(rcx,rcy,4);

  /* put on the numbers */
  /* some adjustment is required in x and y axis */
  /* the initial calculation provides the center point (datum)
     for each string in pixels based on a theoretical circle */
  /* fonts break on 7 bit pixel byte boundaries so fine tuning
     is necessary to calculate xorigin in even byte boundaries
     due to potential underflow of the xterm co-ordinate when dividing.
     this is simply the way graphics work on the apple II.
     */
   xfactor = 7;
   for(idx=1;idx<13;idx++)
   {

     mknumber(idx,&numbuf[0]);
     if (idx > 9)xfactor = 14;

     /* chord circle by 30 degrees for 12 segments */
     tempdegrees= (float)30*idx;
     x = rcx;
     y = rcy;
     circlepoints(&x,&y,letterbase,tempdegrees);
     /* round up x to a multiple of 7 for divide */
     while (x%7 != 0)x++;
     /* convert pixels to bytes */
     x = (x + x - xfactor)/14; /* preserve significant digits */
     /* y is easy since rasters and pixels co-relate */
     y = y-4;
     plots(numbuf,x,y,1);
    }

    /* save a copy of the clock face to erase the hands */
    putimage((char *)&face[0],FACE_WIDTH,FACE_HEIGHT,FACE_X,FACE_Y,1);

    return 0;
}

<< Back to Apple II Graphics

<< Back to Apple Oldies


© Copyright Bill Buckels 2010
All Rights Reserved.