| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# tsrclock.htm
Header
Interrupt
Initialization

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.

Format
  COM TSR
Platform
  DOS. It also works in console of 32bit Windows.
See also
  tsrup as a sample of more sofisticated TSR program.
Build
  `euroasm tsrclock.htm`
Run
  `tsrclock.com`

Header
  This is a program prologue. Code which provides the actual service job is located at the beginning of TSR program and it belongs to the resident memory. The rest (initialization, splash screen etc) is not resident and will be discarded when the program ends.

             EUROASM DumpWidth=21,CPU=186
             INCLUDE "biosapi.htm","dosapi.htm" ; Uses BiosAPI and DosAPI.
    tsrclock PROGRAM Format=COM
    [COM]      SEGMENT Purpose=CODE+DATA+STACK
    %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
              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.

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,'$'
    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

▲Back to the top▲