home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1993 May
/
SIMTEL_0593.ISO
/
msdos
/
deskaces
/
remind.asm
< prev
next >
Wrap
Assembly Source File
|
1986-06-13
|
9KB
|
375 lines
---------------------------- 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) -------------------------