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

	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
coords	dw 0			; original cursor location

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,3		; get cursor position
	mov bh,0
	int 10h
	mov coords,dx		; save it

	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

	mov ah,2		; move to row 1, column	70
	mov bh,0
	mov dx,69
	int 10h

	mov si,offset dhours	; display time
	mov bl,70h		; color	attribute
	mov dx,11		; loop count

p065:	mov al,[si]
	mov ah,9
	mov cx,1
	push si
	int 10h			; write	char/attr.

	push dx
	mov ah,3		; get cursor position
	int 10h
	inc dl			; advance 1 column
	mov ah,2
	int 10h
	pop dx

	pop si
	inc si
	dec dx
	cmp dx,0
	jnz p065

	mov dx,coords		; restore cursor position
	mov bh,0
	mov ah,2
	int 10h

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
	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 - Copyright 1983 Data Base Decisions',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
