Alarms Module
This module is responsible for determining which zones, doors and windows are secure, and handling anu security violations which occur. Zones are secure if all doors connecting to that zone are either secure, or have a secure zone on the other side of them. Doors become secure if they are shut when either they are locked, or have a secure zone on both sides. Windows become secure if they are shut when either in a secure zone, or in a zone that is locked if that zone has the "maximum window security" option set.
When an alarm is triggered, a corresponding bit in the data bytes tq, tq1...tq4 is set (Smoke, panic, windows, internal doors, external doors). Then "topalarm" is called to locate the alarm which has top priority. If that alarm is not already active, then it is set off by the system. After all alarms are cleared, the system returns to the state that it was in before the alarm occurred.
The auto arming feature is also implemented here. When the clock module clicks over to a new minute, a flag is set to indicate that the auto arming needs to be checked on the next pass through the main program loop.
Download:
; /////////////////////////////////////////////////////////////
; // Alarms Module
; // (c) 1999 Geoff Knagge, Andrew Carr, Mark Lynn
; // last modified : 22/10/1999
; //
; /////////////////////////////////////////////////////////////
A_zone: .byte 0
chkAlarms:
push bc
push de
push hl
push af
call recalculate; work out what's secure
;//////////////////////////////// Smoke alarm
lda smokestat
cpi 0 ; 0 = armed
jnz ca_j0 ; jump if not armed
in keyport ; read smoke alarm
ani 80 ; mask the other bits
jz ca_j0 ; is there smoke
mvi e,0 ; yep - set alarm type 0
mvi c,0 ; set item number 0
call ca_queue ; add smoke alarm to queue (e=type, c=item)
cA_j0: ;//////////////////////////////// panic detect
lhld times2 ; miliseconds that the key's been bown
lxi bc,0F448 ; BL = -3000
dad bc ; time down - 3000
jnc ca_p1 ; has it been down longer than 3seconds
mvi e,1 ; yes, alarm type 1
mvi c,0
call ca_queue
;////////////////////////////////
ca_p1: lxi bc,0 ; for window = 0 to 7
mvi e,2 ; alarm Type 2
ca_j1: call SW_Alarm ; check for breaches
cpi 0FF ; is there a problem
jnz ca_j2 ; if not, keep checking
call ca_queue ; otherwise, add item c, type e to alarm queue
ca_j2: inr c ; next one
mvi a,8 ; have we gone too high?
cmp c
jnz ca_j1 ; if not, keep checking
;////////////////////////////////
lxi bc,0 ; initialise 0 to 7 loop
mvi e,3 ; alarm Type 3
ca_j3: call SDgCnZone ; get the door's connecting zone
cpi OutZone ; does it connect to outside?
jz ca_j4 ; if so, don't check it
call SD_Alarm ; otherwise, check for breaches
cpi 0FF ; is there a problem
jnz ca_j4 ; if not, keep checking
call ca_queue ; otherwise, add to alarm queue
ca_j4: inr c ; next one
mvi a,8 ; have we gone too high?
cmp c
jnz ca_j3 ; if not, keep checking
;////////////////////////////////
lxi bc,0 ; initialise 0 to 7 loop
mvi e,4 ; alarm Type 4
ca_j5: call SDgCnZone ; get the door's connecting zone
cpi OutZone ; does it connect to outside?
jnz ca_j6 ; if not, don't check it
call SD_Alarm ; otherwise, check for breaches
cpi 0FF ; is there a problem
jnz ca_j6 ; if not, keep checking
call ca_queue ; otherwise, add to alarm queue
ca_j6: inr c ; next one
mvi a,8 ; have we gone too high?
cmp c
jnz ca_j5 ; if not, keep checking
;////////////////////////////////
call topalarm
call talist
pop af
pop hl
pop de
pop bc
ret
;////////////////////////////////////
tq : .byte 00 ; level 0 trigger bits
tq1: .byte 00 ; level 1 trigger bits
tq2: .byte 00 ; level 2 trigger bits
tq3: .byte 00 ; level 3 trigger bits
tq4: .byte 00 ; level 4 trigger bits
tq5: .byte 00 ; level 5 trigger bits
t_masks:.byte 01,02,04,08,10,20,40,80
ca_queue: ; input: e=type, c = item number
; sets bit C of tq byte #E, and generate a
; UART message if it wasn't already set
push bc
push de
push hl
push af
mvi d,0
lxi hl,tq ;point to array of violations
dad de ;point to right byte
mov a,m ;load the byte into A
mvi b,0 ; BC = item number
mov d,a ; D = tq byte
lxi hl,t_masks
dad bc ; point to right mask - item BC
ana m ; check the bit - TQ byte AND mask
cpi 0 ; is it set? 0 = no
jnz caq_1 ; yes, no need to add it
mov a,d ; retore byte
ora m ; set bit
mvi d,0 ; DE = type
lxi hl,tq ; point HL to array
dad de ; move to right byte
mov m,a ; save new status
;//// here is where we would send a UART message
mov a,e
; cpi 00 ; type 0? (fire)
; cz U_fire
cpi 01 ; type 1? (panic)
cz u_panic
cpi 02 ; type
cz u_intrusion
cpi 03 ; type
cz u_intrusion
caq_1: pop af
pop hl
pop de
pop bc
ret
;////////////////////////////////////
taJump: .word tasmoke, tapanic, tawindow, taidoor, taodoor, nomode
topalarm: ; find the top priority violation and trigger that alarm
; if it is not already activated.
lxi hl,tq
mvi c,0
ta1: mov a,m ; load tq byte for this level
cpi 0 ; zero means no violations
jz ta2 ; if that's the case, keep checking
lxi hl,taJump ; otherwise, point to the jump table
mvi b,0
dad bc
dad bc ; point to the entry for this level
mov c,m ; load the address
inx hl
mov b,m
mov h,b ; move the address to HL
mov l,c
pchl ; jump to it
ta2: inx hl ; next tq byte
inr c
mvi a,5 ; number of alarm types
cmp c
jnz ta1
lda smoked ; to get here, there must be no alarms
cpi 0 ; was there previously a smoke alarm?
jz eta ; no
mvi a,0 ; yes...
sta smoked ; clear the flag
call taRestoreState ; clear the alarm
eta: ret
;/////////////////////////////
alarmed: .byte 0
smoked: .byte 0 ; zero = not triggered, otherwise alarmed
panic: .byte 0
v_win: .byte 0
v_door: .byte 0
;/////////////////////////////
tasmoke: ; smoke alarm triggered
lda smoked ; 0 = not triggered
cpi 0
jnz eta ; already triggered, don't do it again
call TaSaveState; save current mode, screens, etc
mvi a,0
sta tq ; set tq byte 0 to 0
mvi a,1
sta smoked
mvi c,al_smoke
call au_mode ; set beeper
call u_fire
mvi c,fl_smoke
call bl_mode ; set beeper
mvi c,0D3 ; go to "cancel" mode - asks for the password
call setmode
jmp eta
firstbit: ; input: A=a tq byte
; output: BC=first bit set, 0=first, FF=none
push hl
push af
lxi hl,t_masks
mvi c,0
mov b,a ; save tq byte
fb1: mov a,b ; restore tq byte
ana m ; test the bit for this item?
cpi 0 ; is it set?
jnz fb2 ; yes
inr c ; next item
inx hl
mvi a,8 ; number of items
cmp c ; have we gone too high?
jnz fb1 ; no, check next item
mvi c,0FF ; none found signal
fb2: mvi b,0
pop af
pop hl
ret
tapanic:
lda panic
cpi 0
jnz eta ; already triggered
call TaSaveState
mvi a,0
; sta tq1
mvi a,1
sta panic
lda panicmode
ani 1 ; check audible bit
cpi 0
jz pmcmns ; not set, no audible alarm
mvi c,al_panic
call au_mode
pmcmns:
mvi c,fl_panic
call bl_mode
mvi c,0D5
call setmode
jmp eta
tawindow:
call firstbit ; returns BC = window triggered
lda v_win ; A = last window triggered
cmp c ; is it the same one?
jz eta ; yes, no need to do anything
mvi a,0FF
cmp c ; double check that there really was one
jz eta ; if not, do nothing
call TaSaveState
mov a,c ; a = window number
sta v_win ; save it
call SWgCtZone ; get the zone of window C in A
sta a_zone ; set alarmed zone
mvi c,0D9 ; alarm trigger mode
call setmode ; set it
jmp eta
taidoor: ; internal door
call firstbit
lda v_door ; load the last window triggered
cmp c ; is it the same one?
jz eta ; yes, no need to do anything
mvi a,0FF
cmp c ; double check that there really was one
jz eta ; if not, do nothing
call TaSaveState
mov a,c ; a = window number
sta v_door ; save it
call SDgCtZone ; get the zone
sta a_zone ; set alarmed zone
mvi c,0D9 ; alarm trigger mode
call setmode ; set it
jmp eta
taodoor: ; external door - timeout before alarm
call firstbit
lda v_door ; load the last window triggered
cmp c ; is it the same one?
jz eta ; yes, no need to do anything
mvi a,0FF
cmp c ; double check that there really was one
jz eta ; if not, do nothing
call TaSaveState
mov a,c ; a = window number
sta v_door ; save it
call SDgCtZone ; get the zone
sta a_zone ; set alarmed zone
mvi c,0DA ; timout trigger mode
call setmode ; set it
jmp eta
;/////////////////////////////
taSaveState:
push af
mvi a,0
sta smoked ; clear smoke alarm triggered flag
sta panic ; clear panic alarm triggered flag
mvi a,0FF
sta v_win ; clear last window triggered
sta v_door ; clear last door triggered
lda alarmed ; is any alarm currently on?
cpi 0
jnz tass1 ; yes, no need to save states
mvi a,1 ; no, need to remember what was interrupted
sta alarmed ; set flag
lda lcd_ws ; get current screen code
sta ac_lastscreen ; save screen code
lda modecode
cpi 03 ; is mode password input?
jz tasspi ; yes, we need a special handler
sta ac_lastmode ; otherwise save mode code
tass1: pop af
ret
tasspi: ; password input mode - set lastmode
; to the cancel mode
lhld pw_table ; point to the password table
inx hl ; point to the cancel mode
mov a,m
sta ac_lastmode ; set lastmode to cancel
jmp tass1
;////////////////////////////////////
taRestoreState:
push bc
push af
lda ac_lastmode
mov c,a
call setmode
lda ac_lastscreen
mov c,a
call lcd_setmsg
mvi c,0
call au_mode ; stop beeper
mvi c,0
call bl_mode ; stop beeper
mvi a,0
sta alarmed ; clear alarmed flag
sta tq1
sta tq
mvi a,0FF
sta v_door
sta v_win
pop af
pop bc
ret
;//////////////////////////////////////
taList: ;updates the scrolling list of violations
mvi c,0 ; count of violations
mvi b,0
lda tq ; load smoke byte
cpi 0
jz tal1
lxi hl,PSlist
mvi a,0AB
mov m,A
inr c
tal1: lda tq1 ; load panic byte
cpi 0
jz tal2
lxi hl,PSlist
dad bc
mvi a,0AC
mov m,A
inr c
tal2: lda tq2
mov d,a
mvi e,0 ; window number
lxi hl,t_masks ; point to mask array
tal4: mov a,d ; restore tq byte
ana m ; check the bit
cpi 0 ; is there a violation?
jz tal3 ; no
;/// add a line
;/// at this point, C = count of violation, E=window
push de
push hl
mov a,e
push bc
mov c,a ; C = window number
adi 0A3 ; A = message number
mov d,a ; D = message number
call SWgCtZone ; A = controlling zone
pop bc
;// change the zone number in the message
push hl
push de
push bc
push af
lxi hl,messagebank
mov c,D
mvi b,0
mvi e,0
push af
uuu: dad bc
inr e
mvi a,msglength
cmp e
jnz uuu
pop af
dcx hl
dcx hl
dcx hl
adi 31 ; convert to ASCII
mov m,a
pop af
pop bc
pop de
pop hl
;// now need to add message D to the list
;// C=violation count
lxi hl,PSlist
mvi b,0
dad bc ; point to next empty entry
mov m,d ; save message number
inr c ; increase count of violations
pop hl
pop de
tal3: inr e ; next window
inx hl
mvi a,8 ; number of windows
cmp e ; gone too high?
jz tal4 ; no, check another
wal2: lda tq3
mov d,a
lda tq4
ora d ; combine bits for all doors
mov d,a
mvi e,0 ; door number
lxi hl,t_masks ; point to mask array
wal4: mov a,d ; restore tq byte
ana m ; check the bit
cpi 0 ; is there a violation?
jz wal3 ; no
;/// add a line
;/// at this point, C = count of violation, E=door
push de
push hl
mov a,e
push bc
mov c,a ; C = door number
adi 09B ; A = message number
mov d,a ; D = message number
call SDgCtZone ; A = controlling zone
pop bc
;// change the zone number in the message
push hl
push de
push bc
push af
lxi hl,messagebank
mov c,D
mvi b,0
mvi e,0
push af
www: dad bc
inr e
mvi a,msglength
cmp e
jnz www
pop af
dcx hl
dcx hl
dcx hl
dcx hl
adi 31 ; convert to ASCII
mov m,a
pop af
pop bc
pop de
pop hl
;// now need to add message D to the list
;// C=violation count
lxi hl,PSlist
mvi b,0
dad bc ; point to next empty entry
mov m,d ; save message number
inr c ; increase count of violations
pop hl
pop de
wal3: inr e ; next window
inx hl
mvi a,8 ; number of windows
cmp e ; gone too high?
jz wal4 ; no, check another
;/////////////////////
inr c ; also count the blank line
mov a,c
sta PScnt
;////////// if pscnt < top scroll line....
lda modecode
cpi 03 ; if mode <> password input
jnz tal5 ; skip the next section
lda lcd_sline
sub c
jc tal5
mvi a,0
sta lcd_sline ; reset lcd_sline
tal5:
ret
;//////////////////////////////////////
ac_alarm: .byte 0,0 ; type, item
ac_lastmode : .byte 0
ac_lastscreen: .byte 0
;/////////////////////////////////////
CaaEvenInterval:
; checks if the value in D is a multiple of E
mov a,d
cpi 0
jz cei1 ; zero is always a multiple...
cei2: cmp e
jz cei1 ; A = E. Therefore, a multiple
sub e
jnc cei2 ; jump if no overflow
mvi a,0FF ; error. not a multiple
ret
cei1:
mvi a,00
ret
;///////////////////////////
ChkaArm:
lda ccaatt
cpi 0 ; has a new minute passed?
jnz caa9 ; yes, check arming
ret
caa9: ;////////// check smoke alarm
lda smoketime
lxi hl,c_mins
cmp m
jnz caa15
mvi a,0
sta smokestat
;////////// check the rest
caa15: mvi a,0
sta ccaatt ; reset flag
lxi bc,0
caa5: lxi hl,A_times
dad bc
dad bc
lda c_hours ; clock hours
mov e,m
cmp e ; does aaHour = c_hours?
jz caa3 ; if so, check minutes
sub e ; C_hours - aaHour
jnc caa2 ; no carry - aaHour < c_hours
jmp caa4
caa3: ; hours equal, check minutes
inx hl
lda c_mins ; clock minutes
mov e,m
cmp e ; does aaMin = c_mins?
jz caa4 ; if so, check minutes
sub e ; C_mins - aaMin
jc caa2 ; carry set : aaMin > c_mins
caa4: ; we're above or eqaul the start time
lxi hl,a_ints
dad bc ; point to right interval
mov a,m
cpi 0 ; 0 = no auto arm
jz caa2
push af
lxi hl,a_times
dad bc
dad bc
lda c_hours
sub m
mov d,a ; d = this hour - start hour
pop af ; af = aarm mode
mvi e,24.
cpi 1 ; 1 = Once per day
jz caaC
mvi e,12.
cpi 2 ; 2 = Every 12 Hours
jz caaC
mvi e,6
cpi 3 ; 3 = Every 6 Hours
jz caaC
mvi e,3
cpi 4 ; 4 = Every 3 Hours
jz caaC
mvi e,2
cpi 5 ; 5 = Every 2 Hours
jz caaC
cpi 6 ; 6 = Every Hour
jz caaC
push af
lxi hl,a_times
dad bc
dad bc
inx hl
lda c_mins
sub m
jnc xxx_of ; no overflow
adi 60. ; if overflowed, add 60 decimal
xxx_of: mov d,a ; d = this min - start min
pop af ; af = aarm mode
mvi e,30.
cpi 7 ; 7 = every 30 minutes
jz caaM
mvi e,15.
cpi 8 ; 8 = every 15 minutes
jz caaM
mvi e,10.
cpi 9 ; 9 = every 10 minutes
jz caaM
mvi e,5. ; A = every 5 minutes
caaM: ; check minutes
call caaEvenInterval
cpi 0
jnz caa2 ; failed, don't arm
mov a,c
call SZ_lock
jmp caa2
caaC: ; check hours... if even int, check mins
lda c_mins
lxi hl,a_times
dad bc
dad bc
inx hl
cmp m
jnz caa2
call caaEvenInterval
cpi 00
jnz caa2 ; failed, don't arm
mov a,c
call SZ_lock
caa2: ; this zone hasn't reached start time
inr c ; next zone
mvi a,8 ; number of zones
cmp c ; done them all?
jnz caa5 ; no, do the next
ret
|