EuroAssembler Index Manual Download Source Macros

Sitemap Links Forum Tests Projects


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.

DOS. It also works in console of 32bit Windows.
See also
tsrup as a sample of more sofisticated TSR program.
euroasm tsrclock.htm
         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.
Interrupt routine

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.

Interrupt handlers should perform very fast, without waiting for user interaction or huge data manipulation.

When routine NewInt08 is called by hardware-invoked interruption, CS:IP points to NewInt08, other registers are undefined and should be preserved.
Instruction 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 at installation-time.

Memory between PSP (CS:0) and CS:TsrTop remains permanently resident in memory until reboot.

NewInt08 PROC Dist=Far
          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
          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.
            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
        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.
        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.
Initialization routine
This part of TSR program runs only once, when the program is launched.
TSRclock is a most primitive skeleton, it doesn't prevent itself from being installed multiple times. Program installs itself to conventional (low) memory and each tsrclock instance together with its environment segment permanently occupies 0x0170 bytes (see MEM /D). It cannot be uninstalled or installed to high memory above videoram.
Message$ DB "Resident clock installed.",13,10,'$'
         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

▲Back to the top▲