/* LedClock (C)1987 Ali T. Ozer. A clock program for interlaced screens. */ #include #include #include #include #include #include /* LedClock is freely distributable and NOT for sale. ** ** Author: Ali T. Ozer, ARPA: ali@score.stanford.edu, ** REAL: Box 7703, Stanford, CA 94309 ** ** A clock program for interlaced screens. This one uses ** numbers that resemble the numbers used in 7-segment LED and LCD ** clock displays. Various options are provided as compile time "#define"s: ** ** TWENTYFOURHOUR 0 or 1, determines if the clock is 24-hour or not ** WINDOWXLOC starting X location for the window in the WB screen ** WINDOWYLOC starting Y location for the window in the WB screen ** FANCYPANIC 0 or 1, determines if a error message box is shown in ** case the clock can't run... ** ** LedClock works best with interlaced screens --- It will look ugly on a ** "normal" WorkBench screen. (It will probably look fine on a lo-res, non ** interlaced screen, except it will be too big, way too big.) ** ** LedClock can be compiled by Manx 3.40a. Use the "+c" and "+d" switches ** while linking to assure that the static images go into CHIP ram: ** ** cc ledclock.c ** ln +cd ledclock.o -lc ** run ledclock ** ** During linking you'll get warnings about _cli_parse and _wb_parse being ** redefined; you can safely ignore these messages. ** ** Various corners have been cut from this program to make the executable small. ** With Manx 3.40a, the executable is somewhere around 3.1 Kbytes. One can ** probably achieve half that size in assembly. ** ** Programmers might wish to fool around with the code to customize the ** clock. It's amazing how many parameters one can think of if one tries ** to make a general program... So, rather than trying to provide zillions ** of command line arguments, I took the easy way out and just threw out ** all generality... If you wish to create your customized clocks from ** this code, feel free to do so --- but please keep my original copyright ** notice (in the screen title bar) intact, along with any additional ** copyright notices you might add. */ /* The next few define's can be changed for different runtime parameters... */ #define TWENTYFOURHOUR 1 #define FANCYPANIC 0 #define WINDOWXLOC 20 #define WINDOWYLOC 14 #define COPYRIGHT "LedClock 1.0 (C)1987 Ali T. Ozer" #if FANCYPANIC #define Panic(arg) WarnUser(arg) #else #define Panic(arg) CloseThings(1) #endif #define DIGITMAXX 18 #define DIGITMAXY 32 #define WINDOWX 0 #define WINDOWY 9 #define DIGIT1X (WINDOWX + 3) #define DIGIT2X (DIGIT1X + DIGITMAXX + 3) #define DIGIT3X (DIGIT2X + DIGITMAXX + 10) #define DIGIT4X (DIGIT3X + DIGITMAXX + 3) #define DIGITY (WINDOWY + 3) #define WINDOWWIDTH (DIGIT4X + DIGITMAXX + 3) #define WINDOWHEIGHT (DIGITY + DIGITMAXY + 3) #define COLONX (DIGIT3X - 6) #define COLONY (DIGITY + 11) /* The "leds" (the segments making up the digits) are named in the standard ** (clockwise) fashion, with a = top led, b = right top, ... f = left top, and ** g = middle. ** ** The following describes the vertical leds ("b", "c", "e", and "f") */ USHORT vled [] = { 0xdfff, 0x8fff, 0x8fff, 0x8fff, 0x8fff, 0x8fff, 0x8fff, 0x1fff, 0x1fff, 0x1fff, 0x1fff, 0x1fff, 0x1fff, 0xbfff }; /* The following is the bitmap description of leds a and d. */ USHORT hled [] = {0x807f, 0x003f, 0x807f}; /* The following is the bitmap description of the middle ("g") led. */ USHORT mled [] = {0x80ff, 0x007f, 0x80ff}; /* The following describes the bitmap for the colon and the image. */ USHORT colondata [] = { 0x8fff, 0x8fff, 0x8fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x1fff, 0x1fff, 0x1fff }; struct Image colon = { 0, 0, 4, 11, 1, colondata, 1, 0, NULL}; /* The following describes the Image structures for all 7 leds, "a".."g" */ struct Image leds [] = { { 6, 0, 10, 3, 1, &hled[0], 1, 0, NULL}, {15, 2, 4, 14, 1, &vled[0], 1, 0, NULL}, {13, 17, 4, 14, 1, &vled[0], 1, 0, NULL}, { 3, 30, 10, 3, 1, &hled[0], 1, 0, NULL}, { 0, 17, 4, 14, 1, &vled[0], 1, 0, NULL}, { 2, 2, 4, 14, 1, &vled[0], 1, 0, NULL}, { 5, 15, 9, 3, 1, &mled[0], 1, 0, NULL} }; /* In the following each byte determines what leds are lit for the ** corresponding digit. The bits are ordered "xgfedcba" and a 1 means the ** led is lit. */ UBYTE lled [] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; struct Window *MyWin; struct RastPort *MyRP; struct Library *IntuitionBase; struct Library *GfxBase; struct timerequest MyTR; unsigned long WindowSig, TimerSig; int curminutes; /* Draw digit puts a 7-segment digit at the specified location. If digit is ** negative, then the area is erased but nothing is drawn. */ DrawDigit (rp, digit, xloc, yloc) struct RastPort *rp; int digit, xloc, yloc; { register int ledmask, ledcnt = 0; /* First erase it */ RectFill (rp, (long)xloc, (long)yloc, (long)(xloc + DIGITMAXX), (long)(yloc + DIGITMAXY)); if (digit >= 0) { ledmask = lled[digit]; while (ledcnt != 7) { if (ledmask & 1) DrawImage (rp, &leds[ledcnt], (long)xloc, (long)yloc); ledcnt++; ledmask >>= 1; } } } /* OpenTimer will open the timer device and also do all the initialization ** necessary to send timer requests... */ OpenTimer (tr) struct timerequest *tr; { struct MsgPort *timerport; while ((timerport = CreatePort (NULL, 0L)) == NULL) Panic ("No port"); OpenDevice (TIMERNAME, UNIT_VBLANK, tr, 0L); tr->tr_node.io_Message.mn_ReplyPort = timerport; tr->tr_node.io_Command = TR_ADDREQUEST; } /* OpenThings opens the Amiga libraries, the window. and the timer. ** If something goes wrong, a requester will pop up to warn the user. ** (If FANCYPANIC is defined as zero, then no requester pops up --- the ** program just quits.) */ OpenThings () { static struct NewWindow MyWinInfo = { WINDOWXLOC, WINDOWYLOC, WINDOWWIDTH, WINDOWHEIGHT, /* Lft,Top,Wd,Hgt */ -1,-1, /* Detail pen, Block pen (-1 = use screens) */ CLOSEWINDOW, /* IDCMPflags */ SMART_REFRESH | WINDOWDEPTH | WINDOWDRAG | WINDOWCLOSE | NOCAREREFRESH, NULL, NULL, NULL, /* FirstGadget, Menu Checkmark, Title */ NULL, NULL, /* Screen, Bitmap */ 0, 0, 0, 0, /* Min Width/Height Max Width/Height */ WBENCHSCREEN /* Type */ }; if (((IntuitionBase = OpenLibrary("intuition.library",0L)) == NULL) || ((GfxBase = OpenLibrary("graphics.library",0L)) == NULL)) CloseThings (1); while (!(MyWin = OpenWindow(&MyWinInfo))) Panic ("No window"); SetWindowTitles (MyWin, NULL, COPYRIGHT); MyRP = MyWin->RPort; WindowSig = 1L << MyWin->UserPort->mp_SigBit; SetOPen (MyRP, 1L); SetAPen (MyRP, 1L); OpenTimer(&MyTR); TimerSig = 1L << MyTR.tr_node.io_Message.mn_ReplyPort->mp_SigBit; } /* SendTimerRequest sends a timer request for the indicated amount of seconds. ** Assumes the timer request block pointed to by tr has already been properly ** setup --- including the io_Command field. */ SendTimerRequest (tr, seconds) struct timerequest *tr; unsigned long seconds; { tr->tr_time.tv_micro = 0L; tr->tr_time.tv_secs = seconds; SendIO (tr); } #if FANCYPANIC /* WarnUser is called when something goes wrong during initialization. ** WarnUser assumes Intuition is opened! (Now if that's not the case, ** then you're in trouble...) */ WarnUser (reason) UBYTE *reason; { static struct IntuiText postxt = {3,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Retry",NULL}; static struct IntuiText negtxt = {3,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Sigh",NULL}; static struct IntuiText bodytxt = {3,1,COMPLEMENT,4,6,NULL,NULL,NULL}; bodytxt.IText = reason; if (AutoRequest (NULL,&bodytxt,&postxt,&negtxt,0L,0L,300L,60L) == FALSE) CloseThings (1); } #endif /* CloseTimer closes the timer pointed to by tr. If no reply port is present, ** then CloseTimer assumes the timer was never opened. */ CloseTimer (tr) struct timerequest *tr; { struct MsgPort *msgp; if (msgp = tr->tr_node.io_Message.mn_ReplyPort) { DeletePort (msgp); CloseDevice (tr); } } /* CloseThings releases all the resources obtained by the program. */ CloseThings(error_code) int error_code; { CloseTimer(&MyTR); if (MyWin) CloseWindow(MyWin); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); exit (error_code); } static int ht = -1, ho = -1, mt = -1, mo = -1; /* WriteTime writes out the new time. */ WriteTime (minutes) int minutes; { int hours, htens, hones, mtens, mones; #if TWENTYFOURHOUR hours = minutes / 60; #else hours = (minutes / 60 + 11) % 12 + 1; #endif /* AM if minutes < 720, but we don't worry about this... */ if ((htens = hours / 10) != ht) DrawDigit (MyRP, ((ht = htens) ? ht : -1), DIGIT1X, DIGITY); if ((hones = hours % 10) != ho) DrawDigit (MyRP, ho = hones, DIGIT2X, DIGITY); if ((mtens = (minutes % 60) / 10) != mt) DrawDigit (MyRP, mt = mtens, DIGIT3X, DIGITY); if ((mones = (minutes % 10)) != mo) DrawDigit (MyRP, mo = mones, DIGIT4X, DIGITY); } /* ShowTheTime reads the time, updates the display, and sends a new timer ** request. */ ShowTheTime () { long timevec[3]; DateStamp (&timevec[0]); curminutes = (int)(timevec[1]); WriteTime (curminutes); /* Timer request for the next top of minute */ SendTimerRequest (&MyTR, 60L - (long)(timevec[2] / 50L)); } main () { struct Task *me; OpenThings (); /* Bump the priority up to 5. Not too serious, as under normal conditions ** this program will wake up only every 60 seconds. */ if (me = FindTask (NULL)) SetTaskPri (me, 5L); /* Clear the window and draw in the ":". */ RectFill (MyRP, (long)WINDOWX, (long)WINDOWY, WINDOWWIDTH-1L, WINDOWHEIGHT-1L); DrawImage (MyRP, &colon, (long)COLONX, (long)COLONY); /* Now sit down, relax, and wait for either an IDCMP or a timer event... */ while (1) { ShowTheTime (); if (Wait (WindowSig | TimerSig) & TimerSig) (void) GetMsg (MyTR.tr_node.io_Message.mn_ReplyPort); else { AbortIO (&MyTR); /* Don't even get the message, assume it's CLOSEWINDOW */ CloseThings (0); } } } /* Including the following two lines prevents the linker from bringing in ** the two functions _wb_parse and _cli_parse called by the initialization ** code in _main. Reduces code size by about 900 bytes for Manx 3.40a. */ void _wb_parse () {} void _cli_parse () {}