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