cseg segment para public 'code' org 100h alarm proc far ; Memory-resident program to intercept the timer interrupt and display the ; system time in the upper right-hand corner of the display. ; This program is run as 'ALARM hh:mm x', where hh:mm is the alarm time and ; x is '-' to turn the display off. Any other value of x or no value will ; turn the clock on intaddr equ 1ch*4 ; interrupt address segaddr equ 62h*4 ; segment address of first copy mfactor equ 17478 ; minute conversion factor * 16 whozat equ 1234h ; signature color equ 14h ; color attribute assume cs:cseg,ds:cseg,ss:nothing,es:nothing jmp p150 ; start-up code jumpval dd 0 ; address of prior interrupt signature dw whozat ; program signature state db 0 ; '-' = off, all else = on wait dw 18 ; wait time - 1 second or 18 ticks hour dw 0 ; hour of the day atime dw 0ffffh ; minutes past midnite for alarm acount dw 0 ; alarm beep counter - number of seconds (5) atone db 5 ; alarm tone - may be from 1 to 255 - the ; higher the number, the lower the frequency aleng dw 8080h ; alarm length (loop count) may be from 1-FFFF dhours dw 0 ; display hours db ':' dmins dw 0 ; display minutes db ':' dsecs dw 0 ; display seconds db '-' ampm db 0 ; 'A' or 'P' for am or pm db 'm' tstack db 16 dup('stack ') ; temporary stack estack db 0 ; end of stack holdsp dw 0 ; original sp holdss dw 0 ; original ss p000: ; interrupt code push ax ; save registers push ds pushf push cs pop ds ; make ds=cs mov ax,wait ; check wait time dec ax ; zero? jz p010 ; yes - 1 second has elapsed mov wait,ax ; not this time jmp p080 ; return p010: cli ; disable interrupts mov ax,ss ; save stack mov holdss,ax mov holdsp,sp mov ax,ds mov ss,ax ; point to internal stack mov sp,offset estack sti ; allow interrupts push bx ; save other registers push cx push dx push es push si push di push bp mov ax,18 ; reset wait time mov wait,ax mov al,state ; are we disabled? cmp al,'-' jnz p015 ; no jmp p070 p015: mov ah,0 ; read time int 1ah ; get time of day mov ax,dx ; low part mov dx,cx ; high part mov cl,4 shl dx,cl ; multiply by 16 mov bx,ax mov cl,12 shr bx,cl ; isolate top 4 bits of ax add dx,bx ; now in upper mov cl,4 shl ax,cl ; multiply by 16 mov bx,mfactor ; compute minutes div bx ; minutes in ax, remainder in dx cmp ax,atime ; time to sound the alarm? jnz p020 ; no call p100 ; yes - beep the speaker twice push ax mov ax,acount ; get beep count dec ax ; down by 1 mov acount,ax ; save beep count cmp ax,0 ; is it zero? jnz p018 ; no - keep alarm on mov ax,0ffffh ; turn off alarm mov atime,ax p018: pop ax p020: mov dsecs,dx ; save remainder mov bx,60 ; compute hours xor dx,dx ; zero it div bx ; hours in ax, minutes in dx mov dmins,dx ; save minutes cmp ax,0 ; midnight? jnz p030 ; no mov ax,12 ; yes jmp p040a ; set am p030: cmp ax,12 ; before noon? jb p040a ; yes - set am jz p040p ; noon - set pm sub ax,12 ; convert the rest p040p: mov bl,'p' jmp p040x p040a: mov bl,'a' p040x: mov ampm,bl aam ; fix up hour cmp ax,hour ; top of the hour? jz p060 ; no mov hour,ax call p120 ; beep the speaker once p060: add ax,3030h ; convert hours to ascii xchg ah,al mov dhours,ax mov ax,dmins ; get minutes aam add ax,3030h ; convert to ascii xchg ah,al mov dmins,ax mov ax,dsecs ; get seconds (remainder) xor dx,dx mov bx,60 mul bx mov bx,mfactor div bx ; seconds in ax aam add ax,3030h xchg ah,al mov dsecs,ax xor ax,ax ; check monitor type mov es,ax mov ax,es:[410h] ; get config byte and al,30h ; isolate monitor type cmp al,30h ; color? mov ax,0b000h ; assume mono jz p061 ; its mono mov ax,0b800h ; color screen address p061: mov dx,es:[463h] ; point to 6845 base port add dx,6 ; point to status port mov es,ax ; point to monitor mov bh,color ; color in bh mov si,offset dhours ; point to time mov di,138 ; row 1, col 69 cld mov cx,11 ; loop count p062: mov bl,[si] ; get next character p063: in al,dx ; get crt status test al,1 ; is it low? jnz p063 ; no - wait cli ; no interrupts p064: in al,dx ; get crt status test al,1 ; is it high? jz p064 ; no - wait mov ax,bx ; move color & character stosw ; move color & character again sti ; interrupts back on inc si ; point to next character loop p062 ; done? p070: pop bp ; restore registers pop di pop si pop es pop dx pop cx pop bx cli ; no interrupts mov ax,holdss mov ss,ax mov sp,holdsp sti ; allow interrupts p080: popf pop ds pop ax jmp cs:[jumpval] p100 proc near ; beep the speaker twice call p120 push cx mov cx,20000 p105: loop p105 ; wait around pop cx call p120 push cx mov cx,20000 p106: loop p106 ; wait around pop cx call p120 ret p100 endp p120 proc near ; beep the speaker once push ax push cx mov al,182 out 43h,al ; setup for sound mov al,0 out 42h,al ; low part mov al,atone ; get alarm tone out 42h,al ; high part in al,61h push ax ; save port value or al,3 out 61h,al ; turn speaker on mov cx,aleng ; get loop count p125: loop p125 ; wait around pop ax ; restore original port value out 61h,al ; turn speaker off pop cx pop ax ret p120 endp p150: ; start of transient code mov dx,offset copyr call p220 ; print copyright mov ax,0 mov es,ax ; segment 0 mov di,segaddr+2 ; this program's prior location mov ax,es:[di] ; get prior code segment mov es,ax ; point to prior program segment mov di,offset signature mov cx,es:[di] ; is it this program? cmp cx,whozat jnz p160 ; no - install it call p200 ; set state & alarm int 20h ; terminate p160: mov di,segaddr+2 ; point to int 62h mov ax,0 mov es,ax ; segment 0 mov ax,ds ; get current ds mov es:[di],ax ; set int 62h mov si,offset jumpval mov di,intaddr ; point to timer interrupt mov bx,es:[di] ; get timer ip mov ax,es:[di+2] ; and cs mov [si],bx ; save prior ip mov [si+2],ax ; and cs mov bx,offset p000 mov ax,ds cli ; clear interrupts mov es:[di],bx ; set new timer interrupt mov es:[di+2],ax sti ; set interrupts push ds pop es call p200 ; set state & alarm mov dx,offset p150 ; last byte of resident portion inc dx int 27h ; terminate p200 proc near ; set state & alarm mov si,80h ; point to command line mov ax,0 mov di,0ffffh ; init hours mov bh,0 mov ch,0 mov dh,0 ; : counter mov es:[state],bh ; turn clock on mov cl,[si] ; get length jcxz p210 ; it's zero p203: inc si ; point to next char mov bl,[si] ; get it cmp bl,'-' ; is it a minus? jnz p204 ; no mov es:[state],bl ; turn clock off push dx mov dx,offset msg3 ; print msg call p220 pop dx jmp p206 p204: cmp dh,2 ; seen 2nd colon? jz p206 ; yes - ignore seconds cmp bl,':' ; colon? jnz p205 ; no inc dh cmp dh,2 ; second colon? jz p206 ; yes - ignore seconds push cx push dx mov cx,60 mul cx ; multiply current ax by 60 pop dx pop cx mov di,ax ; save hours mov ax,0 jmp p206 p205: cmp bl,'0' jb p206 ; too low cmp bl,'9' ja p206 ; too high - can be a problem sub bl,'0' ; convert it to binary push cx push dx mov cx,10 mul cx ; multiply current value by 10 add ax,bx ; and add latest digit pop dx pop cx p206: loop p203 ; done yet? cmp di,0ffffh ; any time to set? jz p210 ; no add ax,di ; add hours cmp ax,24*60 jb p209 ; ok mov dx,offset msg1 ; print error message call p220 jmp p210 p209: mov es:[atime],ax ; save minutes past midnight mov ax,5 mov es:[acount],ax ; set alarm count mov dx,offset msg2 ; print set msg call p220 p210: ret p200 endp p220 proc near ; print message push ax mov ah,9 int 21h pop ax ret p220 endp copyr db 'Alarm - Clock',10,13,'$' msg1 db 'Invalid time - must be from 00:00 to 23:59',10,13,'$' msg2 db 'Resetting alarm time',10,13,'$' msg3 db 'Turning clock display off',10,13,'$' alarm endp cseg ends end alarm