programming-examples/assembly/Alarm.asm

374 lines
7.9 KiB
NASM
Raw Normal View History

2019-11-15 12:59:38 +01:00
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