home *** CD-ROM | disk | FTP | other *** search
- ---------------------------- REMIND.DOC (cut here) ----------------------------
- REMIND.COM is a program that can pop up a small reminder on line 25 of
- your screen at a given time. It displays messages via the ROM BIOS, and
- thus will work on any IBM BIOS-compatible machine with any display
- hardware in any graphics mode. The syntax is: REMIND time message,
- where time is one or two hour digits followed by a colon and two minute
- digits. REMIND can also be entered without any arguments, in which case
- the pending, or if none, the previous message is shown. The program
- works by communicating with a resident daemon that steals the timer
- interrupt (1c). This communication is via the multiplex interrupt (2f),
- using multiplex number f0. The daemon is installed as a terminate-and-
- stay-resident program the first time that remind is executed. To run on
- a machine with the Microsoft system card, first use the program SETTIME
- to correctly set the BIOS timer (DOS's TIME command doesn't do this when
- the system card's clock.sys device driver is installed).
-
- Written by Robert Lenoil; June/July 1985.
- ------------------------ end of REMIND.DOC (cut here) -------------------------
-
- ---------------------------- REMIND.ASM (cut here) ----------------------------
- ; Displays user-supplied message on 25th screen line at user-specified time.
- ; Author: Robert Lenoil Date: June, 1985
-
- ;Placed in the public domain, June 1986.
- ;Author's electronic mail address:
- ;USENET: lenoil@mit-eddie.uucp ARPA: lenoil@eddie.mit.edu
-
- DOSFN MACRO FNUM ;macro to make DOS function call
- IF FNUM/256
- MOV AX,FNUM
- ELSE
- MOV AH,FNUM
- ENDIF
- INT 21H
- ENDM
-
- PRINT MACRO TEXT ;macro to print message at ds:text
- MOV BX,OFFSET RESGRP:TEXT
- CALL MSGOUT
- ENDM
-
- ATTRIB EQU 0F0H ;flashing black foreground, white background
-
- BIOSDAT SEGMENT AT 40H
- ORG 6CH
- TIMER_LOW DW ? ;low word of timer count
- TIMER_HIGH DW ? ;high word of timer count
- TIMER_OFL DB ? ;timer has rolled over since last read
- BIOSDAT ENDS
-
- RESGRP GROUP DATA,RESDNT,NONRES
-
- DATA SEGMENT
- ORG 2CH
- ENVSEG DW ? ;seg address of environment
- ORG 70H
- RINT2F DD ? ;address of next in int2f chain
- RINT1C DD ? ;address of real int1c handler
- MSGON DB ? ;zero if message already on
- MSGLOW DW ? ;when to put up message (in timer ticks)
- MSGHIGH DW ?
- MSG DW ? ;ptr to start of message on command line
- MSGLEN DB ? ;message length
- CMDLEN DB ? ;command line length
- CMD LABEL BYTE ;command line
- ORG 100H
- DATA ENDS
-
- RESDNT SEGMENT ;handle timer interrupt
- ASSUME CS:RESGRP,DS:BIOSDAT
-
- ENTRY: JMP NEAR PTR START
-
- ;note that on entry, caller's AX,DX are saved; DS points to BIOS data area
- INT1C: STI
- SUB AX,AX
- CMP MSGON,AL ;has message already been displayed?
- JE EXINT ;yes, exit
- CMP TIMER_OFL,AL ;has timer overflowed?
- JNE EXINT ;yes, exit (it's after midnight)
- MOV AX,TIMER_HIGH
- CMP AX,MSGHIGH
- JB EXINT
- JA DISPLAY
- MOV AX,TIMER_LOW
- CMP AX,MSGLOW
- JB EXINT
-
- DISPLAY: ;it's time: print message
- PUSH BX ;save regs
- PUSH CX
- PUSH DX
- PUSH BP
- PUSH SI
- PUSH DI
-
- MOV BL,2 ;send two beeps
- BEEPLP: MOV AX,0E07H
- INT 10H
- DEC BL
- JNZ BEEPLP
-
- MOV AH,15
- INT 10H ;[BH]=active display page, [AH]=max # columns
- PUSH AX ;[AL]=video mode
- MOV AH,3
- INT 10H ;[DX] = cursorpos
- POP AX
- PUSH DX
- PUSH AX
- MOV SI,MSG ;[SI] = ptr to msg
- MOV CL,MSGLEN
- SUB CH,CH ;[CX] = msg length
- POP AX
- CMP CL,AH ;check if msg longer than screen width
- JLE DISP1
- XCHG CL,AH ;yes, truncate
- DISP1: MOV BL,ATTRIB ;load screen attribute
- CMP AL,4 ;are we in a graphics mode (AL > 3)?
- JB DISP2
- AND BL,7FH ;yes, turn off bit 7 (otherwise characters are
- ;XORed onto screen, which isn't what we want.)
- DISP2: MOV DX,1800H ;set cursorpos to row 24, column 0
- DISPLP: MOV AH,2 ;set cursorpos
- INT 10H
- MOV AH,9 ;function = write char/attrib
- MOV AL,CS:[SI] ;get character
- PUSH SI
- PUSH CX
- MOV CX,1 ;repeat count of one
- INT 10H ;write it
- POP CX
- POP SI
- INC SI ;position to next char
- INC DX ;increment cursorpos
- LOOP DISPLP ;loop till cx=0
-
- POP DX ;restore cursorpos
- MOV AH,2
- INT 10H
-
- MOV MSGON,CH ;set displayed flag
-
- POP DI ;pop regs
- POP SI
- POP BP
- POP DX
- POP CX
- POP BX
-
- EXINT: JMP RINT1C ;jump to real timer tick handler
-
- INT2F: CMP AH,0F0H ;if not our number, chain to next
- JE OUR2F
- JMP RINT2F
-
- OUR2F: CMP AL,0 ;is function Get Installed State?
- JE XINT2F
- PUSH CS ;otherwise load our segment into es
- POP ES
- XINT2F: MOV AL,0FFH ;tell caller that we're installed
- IRET
- RESDNT ENDS
-
- NONRES SEGMENT
- ASSUME DS:RESGRP
- START: ;deallocate environment space
- MOV AX,ENVSEG
- MOV ES,AX
- DOSFN 49H
-
- ;erase 25th screen line
- MOV AH,15
- INT 10H ;[BH]=active display page, [AH]=max # columns
- MOV BL,AH ;save ah
- MOV AH,3
- INT 10H ;[DX] = cursorpos
- PUSH DX
- MOV DX,1800H ;set cursorpos to row 24, column 0
- MOV AH,2
- INT 10H
- SUB CX,CX
- XCHG BL,CL ;[CX]=screen width, [BL]=0
- MOV AH,9 ;write (screen width) chars w/attribute 0
- INT 10H
- POP DX ;restore cursorpos
- MOV AH,2
- INT 10H
-
- MOV AX,0F000H ;perform installation check
- INT 2FH
- CMP AL,0FFH ;are we installed?
- JNE INSTALL
- MOV AX,0F001H ;yes, get segment of resdnt in es
- INT 2FH
- XOR CH,CH ;reset just-installed flag
- JMP SHORT PARSE
-
- ASSUME ES:RESGRP
- INSTALL: ;install resident code
- OR AL,AL ;can we install?
- JNZ CANT ;al != 0; can't install resdnt code
- MOV MSGON,AL ;turn off display flag until we're ready
- DOSFN 352FH ;store address of int2f handler
- MOV WORD PTR RINT2F,BX
- MOV BX,ES
- MOV WORD PTR RINT2F+2,BX
- MOV DX,OFFSET RESGRP:INT2F ;set int2f vector to us
- DOSFN 25H
- DOSFN 351CH ;store address of real int1c handler
- MOV WORD PTR RINT1C,BX
- MOV BX,ES
- MOV WORD PTR RINT1C+2,BX
- MOV DX,OFFSET RESGRP:INT1C ;set int1c vector to us
- DOSFN 25H
- PRINT LOADED
- MOV CH,1 ;flag that we just installed ourself
-
- ;parse command line
- PARSE: SUB BX,BX
- MOV CL,CMDLEN
- CALL EAT_SPACE ;eat initial whitespace
- JC GETHRS
- ;command line is empty. show pending message
- OR CH,CH
- JNZ STAY0 ;just installed ourself; there is no message
- CMP ES:MSGON,0 ;is there a pending message?
- JE NOMSG
- PRINT PENDING ;yes, print "pending"
- JMP SHORT PRCMD
- NOMSG: PRINT LASTMSG ;no, print "last reminder"
- PRCMD: PUSH ES ;get resident segment in ds
- POP DS
- PRINT CMD ;print reminder
- EXIT0: XOR AL,AL ;exit with errorlevel = 0
- EXIT: DOSFN 4CH
-
- CANT: PRINT NOLOAD
- MOV AH,2
- JMP SHORT EXIT
-
- STAY0: MOV AL,0 ;errorlevel = 0
- STAY: MOV DX,OFFSET RESGRP:START ;terminate and stay resident
- MOV CL,4
- SHR DX,CL
- DOSFN 31H
-
- GETHRS: CALL GETDIG1
- CMP CMD[BX],':'
- JE GOTHRS
- CALL GETDIG2
- CMP CMD[BX],':'
- JNE SYNTAX
- GOTHRS: CMP AL,24 ;check for range 0-23
- JGE SYNTAX
- INC BX ;skip colon
- MOV DX,65520
- MUL DX ;convert hours to timer ticks
- MOV MSGHIGH,DX
- MOV MSGLOW,AX
-
- CALL GETDIG1 ;get seconds
- CALL GETDIG2
- CMP AL,60 ;check for range 0-59
- JGE SYNTAX
- MOV DX,1092
- MUL DX ;convert to timer ticks
- ADD MSGLOW,AX ;and add to hours
- ADC MSGHIGH,DX
-
- CMP CMD[BX],20H ;at least one space required
- JNE SYNTAX
- CALL EAT_SPACE ;consume any others
- JNC SYNTAX
-
- LEA AX,CMD[BX] ;store start of message ptr
- MOV MSG,AX
- MOV DL,CL
- SUB DL,BL ;store message length
- MOV MSGLEN,DL
-
- INC MSGON ;all fields are setup, set the display flag
-
- OR CH,CH ;are we the resident code?
- JNZ STAY0 ;yes: we're done
- CMP ES:MSGON,0 ;no: will we overwrite a pending message?
- JE DWNLD
-
- PUSH CX ;yes, print it first
- PRINT OVRWRT
- PUSH ES
- POP DS
- PRINT CMD
- PUSH CS
- POP DS
- POP CX
-
- DWNLD: STD ;no: download msg to resident code
- ;we move backwards so that the display flag is the last byte written
- ADD CL,9
- MOV SI,OFFSET RESGRP:MSGON - 1
- ADD SI,CX
- MOV DI,SI
- REP MOVSB
- JMP EXIT0 ;we're done
-
- SYNTAX: PUSH CX ;save ch
- PRINT SERROR
- POP CX
- MOV AL,1 ;errorlevel = 1
- OR CH,CH ;are we the resident code?
- JNZ GOSTAY ;yes, then stay resident
- JMP EXIT ;else just exit
- GOSTAY: JMP STAY
-
- EAT_SPACE PROC NEAR
- ;Advances cmd[bx] past any spaces. Resets carry if ran off end of cmd.
- CMP BL,CL
- JNC ATE
- CMP CMD[BX],20H
- STC
- JNE ATE
- INC BX
- JMP SHORT EAT_SPACE
- ATE: RET
- EAT_SPACE ENDP
-
- GETDIG1 PROC NEAR
- ;GETDIG1 gets digit in AX. GETDIG2 multiplies AX by 10 and adds new digit.
- SUB AX,AX
- GETDIG2:
- MOV DL,CMD[BX]
- CMP DL,'0' ;check for digit range
- JL SYNTAX
- CMP DL,'9'
- JG SYNTAX
- INC BX
- SUB DL,'0'
- MOV DH,10
- MUL DH
- ADD AL,DL
- RET
- GETDIG1 ENDP
-
- MSGOUT PROC NEAR ;displays string at ds:bx w/length byte at bx-1
- SUB CH,CH ;output message to stderr
- MOV CL,[BX]-1
- MOV DX,BX
- MOV BX,2
- DOSFN 40H
- MOV DL,0DH ;output CRLF to console
- DOSFN 6H
- MOV DL,0AH
- INT 21H
- RET
- MSGOUT ENDP
-
- ;Messages (each preceeded by a byte holding its length)
- DB 27
- SERROR DB "Usage: REMIND hh:mm message"
- DB 34
- NOLOAD DB "System error: Can't install daemon"
- DB 24
- LOADED DB "REMIND daemon installed."
- DB 8
- PENDING DB "Pending:"
- DB 35
- LASTMSG DB "Nothing pending; last reminder was:"
- DB 28
- OVRWRT DB "Overwriting pending message:"
- NONRES ENDS
-
- END ENTRY
- ------------------------ end of REMIND.ASM (cut here) -------------------------
-