TSRclock is a simple Terminate and Stay Resident (TSR) program for DOS which displays current time in the upper-right corner of textmode screen.
It is hooked on timer-interrupt 0x08 and updates the clock on screen every second.
EUROASM DumpWidth=21,CPU=186 INCLUDE "biosapi.htm","dosapi.htm" ; Uses BiosAPI and DosAPI. tsrclock PROGRAM Format=COM %Color %SETA %Magenta | %BrYellow*16 ; How the clock will be displayed. JMP Initialization: ; Fixed entry point of COM programs requires this skip.
New handler for interrupt does the actual TSR's job. The far jump at its end chains this handler to previously installed Int08 handlers (if any) and, finally, to the original BIOS handler which sends acknowledgement to the interrupt controller 8259 and provides the final IRET from the interrupt.
Interrupt 08 is invoked by hardware timer every 55 ms. As we don't need to update the clock on display that often, the update routine will be called only once per 18 interruptions.
When routine NewInt08 is called by hardware-invoked interruption, CS:IP points to NewInt08,
other registers are undefined and should be preserved.
JMP 0:0 near its end (immediate segment:offset in the JMP instruction body)
will be referred as OldInt08 and replaced with current INT 08 vector taken from the interrupt table
Memory between PSP (CS:0) and CS:TsrTop remains permanently resident in memory until reboot.
NewInt08 PROC Dist=Far PUSHF PUSH AX MOV AL,[CS:IntCounter] INC AL MOV [CS:IntCounter],AL CMP AL,18 JB .Skip: CALL Update: ; Once in 18 interruptions (once per second). .Skip: POP AX POPF JMPF 0:0 ; Continue with older INT 08 handlers. OldInt08 EQU $-4 ; Previous vector value is kept in the body of JMPF instruction. ENDP NewInt08 Update: PROC Dist=Near ; Inquire the clock and display the time. PUSH BX,CX,DX,DI,ES MOV [CS:IntCounter],0 ; Reset the counter. BiosAPI AH=0x0F ; Read screen width to AH (40|80|132). SHR AX,8 SUB AX,8 ; Go back 8 characters from the right edge. ADD AX,AX ; Each character on screen takes 2 bytes of memory. MOV DI,AX MOV AX,0xB800 ; Memory of textmode video screen begins at VA=0xB8000. MOV ES,AX ; ES:DI is now address of videoram to write hh:mm:ss. BiosAPI INT=0x1A,AH=2 ; Get real-time clock. CH=hour, CL=min, DH=sec, BCD. MOV AH,%Color ; Videoattribute common for all 8 characters. CLD MOV AL,CH CALL StoBCD: ; Hours. MOV AL,':' STOSW ; Separator. MOV AL,CL CALL StoBCD: ; Minutes. MOV AL,':' STOSW ; Separator. MOV AL,DH CALL StoBCD: ; Seconds. POP ES,DI,DX,CX,BX RET ENDPROC Update: StoBCD: PROC Dist=Near ; Put BCD-encoded number from AL to video memory ES:DI. MOV CH,AL ; Temporary save. SHR AL,4 ; Left digit only. OR AL,'0' ; Convert BCD nibble to a digit character. STOSW ; Put the digit to video RAM. MOV AL,CH ; Restore. AND AL,0x0F ; Right digit only. OR AL,'0' ; Convert BCD nibble to a digit character. STOSW ; Put the digit to video RAM. RET ENDP StoBCD: IntCounter DB 0 ; Incremented on every INT 08 invocation, zeroed when it reaches 18. ALIGN 16 TsrTop: EQU $ ; Memory between PSP and TsrTop remains resident. The rest is retured to DOS when the program terminates.
MEM /D). It cannot be uninstalled or installed to high memory above videoram.
Message$ DB "Resident clock installed.",13,10,'$' Initialization: DosAPI AX=0x3508 ; Get previous interrupt vector 08 to ES:BX. MOV [OldInt08+0],BX MOV [OldInt08+2],ES DosAPI AX=0x2508,DX=NewInt08 ; Establish interrupt vector 08 from DS:DX. DosAPI AH=9,DX=Message$ ; Show the message in terminal. MOV DX,TsrTop ; Amount of residental memory in bytes. SHR DX,4 ; Convert bytes to paragraphs (OWORDs). TerminateStayResident DX ; Leave DX paragraphs in DOS lower memory. ENDPROGRAM tsrclock