******************************************* * This is the source file for a New Desk * * Accessory (NDA) which displays the * * current date and time in a window on * * the GS desktop. After assembling, use * * the APW FILETYPE command to change the * * file type code of CLOCK to $B8, then * * transfer CLOCK to the SYSTEM/DESK.ACCS/ * * directory of the ProDOS 16 boot disk. * * * * by Gary Little April 5, 1987 * * * * (This program is adapted from a similar * * one in the Desk Accessories chapter of * * my "Exploring the Apple IIGS" book, to * * be published in July, 1987 by * * Addison-Wesley.) * * * ******************************************* LIST OFF SYMBOL OFF ABSADDR ON INSTIME ON GEN ON KEEP CLOCK ;Code file MCOPY CLOCK.MAC ;Macro file Period GEQU 60 ;Ask for "run" action every second EventMask GEQU $FFFF ;Handle all events NDA_Clock START DC I4'NDA_Open' ;Open the NDA DC I4'NDA_Close' ;Close the NDA DC I4'NDA_Action' ;Perform NDA action DC I4'NDA_Init' ;Startup/Shutdown the NDA DC I2'Period' ;Periodicity of "run" action DC I2'EventMask' ;Permitted events DC C'##' ;Name in menu item form DC C'Clock' ;Text for NDA name DC C'\H**',I1'0' ;ID field + terminator ; Open the NDA if it has not been previously opened. This routine ; must return a pointer to the NDA window on the stack, just above ; the 3-byte return address. The Desk Manager reserves this result ; space just before calling NDA_Open with a JSL instruction. NDA_Open ANOP Result EQU $05 ;Result stack offset after JSL, PHB PHB PHK PLB ;data bank = code bank LDA ClockOpen ;Clock window already open? BNE Ignore ;if so, branch PHA ;Space for result PHA PushPtr WindowDef _NewWindow ;Create and open NDA window PLX ;Pop pointer (low) PLA ;Pop pointer (high) STX WindowPtr ;Save pointer to window STA WindowPtr+2 STA Result+2,S ;Save result on stack (high) TXA STA Result,S ; (low) PushLong WindowPtr _SetSysWindow ;Mark this as a DA window LDA #$FFFF STA ClockOpen ;Set "open" flag PHA ;space for result PHA _GetCursorAdr PopLong OldCursor ;Save pointer to regular cursor Ignore PLB RTL ; Close the NDA if it is not already closed: NDA_Close ANOP PHB PHK PLB ;data bank = code bank LDA ClockOpen ;Is the clock window open? BEQ Ignore ;No, so branch PushLong WindowPtr _CloseWindow ;Get rid of the window STZ ClockOpen ;Mark clock as closed PushLong OldCursor _SetCursor ;Restore application cursor PLB RTL ; Perform the NDA action: NDA_Action ANOP PHB ;Save data bank PHK PLB ;Make data bank = program bank PHY ;Save incoming parameters PHX ;(event record or menu info) ASL A ;x2 to step into table TAX JSR (ActionTbl,X) PLX ;Fix up the stack PLY PLB RTL ActionTbl ANOP DC I2'NoAction' DC I2'NDA_Event' DC I2'NDA_Run' DC I2'NDA_Cursor' DC I2'NDA_Menu' DC I2'NDA_Undo' DC I2'NDA_Cut' DC I2'NDA_Copy' DC I2'NDA_Paste' DC I2'NDA_Clear' NoAction ANOP RTS NDA_Menu ANOP RTS NDA_Undo ANOP NDA_Cut ANOP NDA_Copy ANOP NDA_Paste ANOP NDA_Clear ANOP ; On exit A=0 if edit command wasn't handled; non-zero if it was. ; You will usually want to say it was handled, because the application ; will not be active and so shouldn't be dealing with edit commands. LDA #$FFFF ;Say we handled it. RTS ; Display a wristwatch cursor if the cursor is over top of ; the content region of the window. This routine is only called ; when the DA window is the front window. NDA_Cursor ANOP PHA PHA _GetPort ;Save current GrafPort PushLong WindowPtr _SetPort ;Make clock window active GrafPort PushPtr PortRect _GetPortRect ;Get the port rectangle (local coords) PushPtr MousePosn ;Return mouse position in GrafPort coords _GetMouse ;Get cursor position PHA ;space for result PushPtr MousePosn ;pointer to mouse coordinate PushPtr PortRect ;pointer to content region rectangle _PtInRect PLA ;Is it in content region? BEQ NDA_Curs1 ;No, so branch ; Switch to watch cursor, but only if it's not already active: PHA PHA _GetCursorAdr ;Get current cursor pointer PLA PLX CMP #WatchCurs ;Is it the watch? BNE NDA_Curs0 ;Definitely not CPX #^WatchCurs ;Is it the watch? BEQ NDA_Curs3 ;Yes, so do nothing NDA_Curs0 PushPtr WatchCurs BRA NDA_Curs2 ; Switch to application cursor, but only if it's not already active: NDA_Curs1 PHA PHA _GetCursorAdr ;Get current cursor pointer PLA PLX CMP #WatchCurs ;Is it the watch? BNE NDA_Curs3 ;No, so don't do anything CPX #^WatchCurs ;Is it the watch? BNE NDA_Curs3 ;No, so don't do anything PushLong OldCursor ;Switch to application cursor NDA_Curs2 _SetCursor NDA_Curs3 _SetPort ;Restore GrafPort RTS ; This subroutine is called once every "Period" ticks: NDA_Run ANOP PHA ;Space for result PHA _GetPort ;Save current port PushLong WindowPtr _SetPort ;Switch to clock window for drawing JSR ShowTime ;Display the new time _SetPort ;(Pointer still on stack) RTS ; X and Y (pushed on stack) contain pointer to event record NDA_Event ANOP TheEvent EQU $05 ;1 (base) + 2 (JSR) + 2 (PHD) PHD TSC TCD ;Align d.p. with stack LDA [TheEvent] ;Get "what" code CMP #9 ;Anything we support? BCS TE1 ;No, so branch ASL A ;x2 to step into table TAX JSR (EventTbl,X) TE1 PLD ;Restore direct page RTS EventTbl ANOP DC I2'NoEvent' ;Not supported DC I2'DoMouseDwn' ; Mouse down DC I2'DoMouseUp' ; Mouse up DC I2'DoKeyDwn' ; Key down DC I2'NoEvent' ;Not supported DC I2'DoAutoKey' ; Autokey DC I2'DoUpdate' ; Update DC I2'NoEvent' ;Not supported DC I2'DoActivate' ; Activate DoMouseUp ANOP DoMouseDwn ANOP DoKeyDwn ANOP DoAutoKey ANOP NoEvent RTS DoUpdate ANOP PushLong WindowPtr _BeginUpdate ;Visible region = update region JSR ShowTime ;Display the current time PushLong WindowPtr _EndUpdate ;Restore entire visible region RTS ; If NDA window is deactivated, return to original cursor. DoActivate ANOP LDY #14 ;Access modifiers field LDA [TheEvent],Y AND #$01 ;Isolate activate/deactivate flag BEQ NDA_Off ;If 0, deactivate RTS NDA_Off PushLong OldCursor _SetCursor ;Switch to previous cursor RTS ShowTime ANOP PushPtr TheTime _ReadASCIITime ;Read the clock SEP #$20 ;8-bit A register for byte accesses LONGA OFF LDY #19 ST1 LDA TheTime,Y AND #$7F ;Convert to standard ASCII STA TheTime,Y DEY BPL ST1 REP #$20 ;Back to 16-bit A register LONGA ON ;Get the name of the day of the week: PHA ;Space for 8 bytes of result PHA PHA PHA _ReadTimeHex PLA ;Pop minute/second PLA ;Pop year/hour PLA ;Pop month/day PLA ;Pop day of week (high byte) XBA ;Put day of week in low byte AND #$0F ;Strip unused bits DEC A ;Convert 1..7 to 0..6 (1=Sunday) ; Look for the Nth entry in the table: TAY LDX #0 SEP #$20 ;Use 8-bit accumulator LONGA OFF Find_DOW CPY #0 ;At correct name? BEQ Move_DOW ;Yes, so branch FE1 LDA DayTable,X BEQ FE2 ;Branch if at end of name INX ;Move to next character BRA FE1 FE2 INX ;Move to start of next name DEY ;Decrement day-of-week counter BRA Find_DOW ; Transfer the name to the buffer area: Move_DOW ANOP LDY #0 SR1 LDA DayTable,X BEQ SR2 ;Branch if at end of name STA TheDay,Y INX INY BRA SR1 SR2 REP #$20 ;Back to 16-bit accumulator LONGA ON PushWord #2 ;horizontal PushWord #9 ;vertical _MoveTo PushPtr TheTime _DrawCString ;Draw the time string PushWord #10 ;horizontal PushWord #19 ;vertical _MoveTo PHA _GetForeColor ;Save foreground color Pushword #5 ;Dark Green letters _SetForeColor PRINTG 'Copyright (c) 1987 Gary B. Little' _SetForeColor ;Restore foreground color RTS ; Startup or shutdown the NDA. On entry, A=0 for DeskShutdown, ; A is nonzero for DeskStartup. NDA_Init ANOP PHB PHK PLB CMP #0 ;Starting up? BNE NDA_Init1 ;Yes, so do nothing LDA ClockOpen ;Clock window open? BEQ NDA_Init1 ;No, so branch PushLong WindowPtr _CloseWindow ;Close the window (releases memory) STZ ClockOpen ;Set "closed" flag NDA_Init1 PLB RTL ; The data area begins here: NDA_Title STR 'Calendar/Clock' ;Window title WindowDef ANOP DC I2'EndWind-WindowDef' DC I2'%1100000010100000' ;Window with close box, title DC I4'NDA_Title' ;Pointer to window name DC I4'0' DC I2'0,0,0,0' DC I4'0' DC I4'0' ;Origin at (0,0) DC I4'0' DC I4'0' DC I4'0' DC I4'0' DC I4'0' DC I2'0' DC I4'0' DC I4'0' DC I4'0' ;(Handle our own updates) DC I'30,33,52,284' ;Dimensions of window DC I4'-1' ;Put clock window in front DC I4'0' EndWind ANOP WindowPtr DS 4 ;Pointer to window record ClockOpen DS 2 ;Used as a flag PortRect DS 8 ;Content region rectangle MousePosn DS 4 ;Current mouse position (local) TheTime DS 20 ;ReadASCIITime returns 20 bytes here DC C' -- ' TheDay DS 9 ;Day of week inserted here DC C' ' ;Add padding DC I1'0' ;(terminator for DrawCString) DayTable ANOP DC C'Sunday ',I1'0' DC C'Monday ',I1'0' DC C'Tuesday ',I1'0' DC C'Wednesday',I1'0' DC C'Thursday ',I1'0' DC C'Friday ',I1'0' DC C'Saturday ',I1'0' OldCursor DS 4 ;Pointer to application's cursor record ; This the is the cursor record for a "wristwatch" cursor: WatchCurs DC I2'12' ;Rows in cursor image DC I2'3' ;Cursor width (in words) DC H'000000000000' ;The cursor image DC H'000FF0000000' DC H'000FF0000000' DC H'00F00F000000' DC H'0F00F0F00000' DC H'0F00F0F00000' DC H'0F0FF0FF0000' DC H'0F0000F00000' DC H'00F00F000000' DC H'000FF0000000' DC H'000FF0000000' DC H'000000000000' DC H'000FF0000000' ;The cursor mask DC H'00FFFF000000' DC H'00FFFF000000' DC H'0FFFFFF00000' DC H'FFFFFFFF0000' DC H'FFFFFFFF0000' DC H'FFFFFFFFF000' DC H'FFFFFFFF0000' DC H'0FFFFFF00000' DC H'00FFFF000000' DC H'00FFFF000000' DC H'000FF0000000' DC I2'6,8' ;Hot spot (y,x) END