---------------------------- 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) -------------------------