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

This is page designed, maintained and
(C)opyright 1999 by Geoff Knagge