; A Level Computing Science Project: Car Burglar Alarm.
;
; This file shows the complete Burglar Alarm Operating
; System, BugOS.
;
; (C) 1987, 2026 Rich Teer.


		ORG		&F000			; Assemebled code to start at &F000.

nmi
reset	SEI						; Disable interrupts.
		CLD						; Clear Decimal mode.
		LDXIM	&FF				; Load X Register with 255,
		TXS						; and reset Stack Pointer.
		LDAIM	&80				; Load Accumulator with 128,
		STA		v1ier			; and disable interrupts from VIA.
		LDAIM	&1F				; Load Accumulator with 31,
		STA		v1ddra			; and initialise DDRA.
		LDAIM	&F0				; Load Accumulator with 240,
		STA		v1ddrb			; and initialise DDRB.
		LDAIM	&00				; Load Accumulator with 0 to
		STAZ	flags			; zero all flags,
		STA		v1iora			; zero I/O port A, and
		STA		v1iorb			; zero I/O port B.
		LDAIM	&10				; Load Accumulator with 16, i.e., 10,000 MOD 256,
		STA		v1t1cl			; and store in counter low.
		LDAIM	&27				; Load Accumulator with 39, i.e., 10,000 DIV 256,
		STA		v1t1ch			; to set up Timer 1 to count for 10,000 cycles.
		LDAIM	&40				; Load Accumulator with 64,
		STA		v1acr			; set T1 to free-run mode, T2 to one-shot mode.
		CLI						; Enable interrups.
		STAIM	&3C				; Load Accumulator with 60.
		JSR		delay			; Wait for 1 minute.
		LDAIM	&03				; A minute has elapsed, so set up alarm.
		STAZ	tries			; Initialise number of password attempts.
		LDAIM	&FF				; Load Accumulator with 255.
		LDXIM	&00				; Load X Register with 0.
setupl	STAAX	enterd			; Store 255 at enterd, X.
		INX						; Increment X, and
		BNE		setupl			; if X not 0, loop again.
		LDAIM	&80				; Load Accumulator with 128.
		STA		v1iorb			; Turn on alarm-set LED.

maloop	LDA		v1iorb			; Get data from VIA 1 I/O register B.
		ANDIM	&0F				; Mask out output bits.
		CMPIM	&0F				; Check for a zero bit.
		BEQ		t4dor1			; If not, check if a door has been opened.
		CMPIM	&0E				; Check if just a door is open.
		BEQ		t4dor2			; If so, see if this is first occurence.
		JMP		alarm2			; Another bit is set so activate alarms.
t4dor1	LDAIM	doors			; Load Accumulator with doors open mask.
		BITZ	flags			; Check to see if a door has been opened.
		BEQ		maloop			; If not, check again,
		JMP		keypad			; else branch to keypad handling routine.
t4dor2	LDAIM	doors			; Load Accumulator with doors open mask.
		BITZ	flags			; Check to see if this first occurence.
		BNE		maloop			; If not, return to the main loop.
		LDAZ	flags			; Load Accumulator with flags,
		ORAIM	doors			; Set doors flag,
		STAZ	flags			; and store Accumulator in flags.
		LDAIM	&1E				; Load Accumulator with 30,
		JSR		timer			; and set timer.
		JMP		maloop			; Poll input bits again.

keypad	LDXIM	&00				; Initialise X Register.
keypdl	LDAIM	&E0				; Load Accumulator with 31, and
		STA		v1ddra			; change DDRA's mask.
		LDAIM	&00				; Load Accumulator with 0,
		STA		v1iora			; and make all outputs low.
		LDA		v1iora			; Load Accumulator with I/O Register A.
		ANDIM	&1F				; Mask out output bits.
		CMPIM	&1F				; Compare with 31.
		BEQ		keypdl			; If 31, all bits are 1, so check again.
		STAZ	temp			; Store Accumulator in temp.
		LDAIM	&1F				; Load Accumulator with 224, and
		STA		v1ddra			; change DDRA's mask.
		LDAIM	&00				; Load Accumulator with 0,
		STA		v1iora			; and make all outputs low.
		LDA		v1iora			; Load Accumulator with I/O Register A.
		ANDIM	&E0				; Mask out output bits.
		CMPIM	&E0				; Compare with 224.
		BEQ		keypdl			; If 224, all bits are 1, so check again.
		CLC						; Clear Carry.
		ADCZ	temp			; Add temp to the Accumulator.
		STAAX	enterd			; Store number in buffer.
		CMPIM	&6F				; Check for a "#".
		BEQ		ckpswd			; If it is, compare entry with password.
		CMPIM	&CF				; Check for a "*".
		BEQ		ckpswd			; If it is, compare entry with password.
		INX						; Increment counter.
kypd12	LDA		v1iora			; Load Accumulator with I/O Register A.
		ANDIM	&E0				; Mask out output bits.
		CMPIM	&E0				; Compare to 224,
		BNE		keypdl2			; and if not 224, check again,
		JMP		keypdl1			; else get another character from keypad.

ckpswd	CPXIM	&00				; Compare number of entered characters to 0.
		BEQ		keypad			; If 0, disallow entry.
		LDAIM	&00				; Load Accumulator with 0,
		STAAX	enterd			; and store at enterd, X to mark end of entry.
		LDXIM	&00				; Initialise X Register.
pswdlp	LDAAX	enterd			; Load Accumulator with Xth character entered.
		BEQ		pswdok			; If 0, correct password has been entered.
		CMPAX	psswrd			; Compare A with Xth character of password.
		BEQ		nxtchr			; If they are the same, get next character.
		CMPAX	stored			; Compare A with alternative password.
		BEQ		nxtchr			; If they are the same, get next character.
		DECZ	tries			; They aren't, so decrement number of tries,
		BNE		keypd			; if tries > 0 then allow another attempt,
		JMP		alarm2			; else turn on alarms.
nxtchr	INX						; Increment X Register,
		JMP		pswdlp			; and compare the next character.
keypd	JMP		keypad			; This gets around the out of range branch.

pswdok	JSR		timoff			; Turn off timer.
		LDAIM	&40				; Load Accumulator with 64.
		STA		v1iorb			; Turn on ignition, turn off alarm active LED,
pwoklp	JMP		pwoklp			; and idle.

timout	STAZ	acc				; Preserve Accumulator.
		STXZ	xreg			; Preserve X Register.
		STYZ	yreg			; Preserve Y Register.
		LDAIM	timer2			; Load Accumulator with timer2 mask,
		BIT		v1ifr			; see if timer2 caused interrupt.
		BNE		tm2int			; If it did, branch to timer2 handler.
		LDAIM	&C0				; Load Accumulator with 192,
		STA		v1ifr			; and clear Timer1 interrupt.
		LDAZ	sec100			; Load Accumulator with 1/100 second counter,
		BEQ		second			; and if zero check seconds.
		DECZ	sec100			; Decrement 1/100 second counter.
		JMP		leave1			; and leave.
second	LDAZ	secs			; Load Accumulator with second counter,
		BEQ		timeup			; and if zero take appropriate alarm action.
		DECZ	secs			; Decrement second counter.
		LDAIM	&64				; Load Accumulator with 100,
		STAZ	sec100			; reset 1/100 second counter,
		JMP		leave1			; and leave.
timeup	LDAIM	clock			; Load Accumulator with clock mask,
		BITZ	flags			; and check to see if bit is set.
		BNE		leave2			; If so, use delay exit routine, else
		JMP		alarms			; turn on alarms.
leave1	LDAZ	acc				; Restore Accumulator.
		LDXZ	xreg			; Restore X Register.
		LDYZ	yreg			; Restore Y Register,
		RTI						; and return.
tm2int	LDAIM	&A0				; Load Accumulator with 160,
		STA		v1ifr			; and clear timer2 interrupt.
leave2	PLA						; Get Processor Status,
		STAZ	sr				; and store.
		PLA						; Get return address Lo,
		STAZ	pclo			; and store.
		PLA						; Get return address Hi,
		STAZ	pchi			; and store.
		CLC						; Clear carry.
		LDAZ	pclo			; Retrieve return address Lo,
		ADCIM	&02				; add 2,
		STAZ	pclo			; and store.
		LDAZ	pchi			; Retrieve return address Hi,
		ADCIM	&00				; adjust for beyond page jumps,
		PHA						; and store.
		LDAZ	pclo			; Retrieve return address from storage,
		PHA						; and store on stack, ready for return.
		LDAZ	sr				; Get Processor Status Register,
		PHA						; and store on Stack.
		LDAZ	acc				; Restore Accumulator.
		LDXZ	xreg			; Restore X Register.
		LDYZ	yreg			; Restore Y Register.
		PLP						; Restore Processor Status register,
		RTS						; and return.

alarms	PLP						; Restore Processor Status register.
		PLA						; Clear garbage
		PLA						; off Stack.
alarm2	JSR		timoff			; Turn off timer.
		LDXIM	&06				; Initialise X Register.
alarm1	LDAIM	&B0				; Load Accumulator with 176,
		STA		v1iorb			; and store in I/O Register B.
		LDAIM	&0A				; Load Accumulator with 10,
		JSR		delay			; and wait for 10 seconds.
		LDAIM	&80				; Load Accumulator with 128,
		STA		v1iorb			; and store in I/O Register B.
		LDAIM	&02				; Load Accumulator with 2.
		JSR		delay			; Wait for 2 seconds.
		DEX						; Decrement X Register, and
		BNE		alarm1			; if not 0, loop again.
		JMP		reset			; Reset the alarm.

delay	STAZ	secs			; Store number of seconds delay required.
		BNE		dnzero			; If required delay isn't 0, skip this bit.
		LDAIM	&01				; Load Accumulator with 1,
		STAZ	sec100			; and initialise 1/100 sec. counter for debounce.
		JMP		dover			; Skip over dnzero.
dnzero	DECZ	secs			; Decrement required delay by 1.
		LDAIM	&64				; Load Accumulator with 100, and
		STAZ	sec100			; initialise 1/100 second counter.
dover	LDAZ	flags			; Load Accumulator with flags.
		ORAIM	clock			; Set delay bit to 1,
		STAZ	flags			; and store in flags.
		JSR		timon			; Turn on the timer.
wait	JMP		wait			; Wait indefinitely, waiting for timer.
		JMP		timoff			; Turn off timer, and return.

timer	STAZ	secs			; Store number of seconds required to be timed.
		DECZ	secs			; Decrement required time by 1.
		LDAIM	&64				; Load Accumulator with 100, and
		STAZ	sec100			; initialise 1/100 second counter.
		LDAIM	clock			; Load Accumulator with clock mask.
		EORIM	&FF				; Do an Accumulator = 255 - clock,
		STAZ	temp			; and store result in temp.
		LDAZ	flags			; Load Accumulator with flags.
		ANDZ	temp			; Reset delay bit to 0,
		STAZ	flags			; and store in flags.

timon	LDAIM	&C0				; Load Accumulator with 192.
		STA		v1ifr			; Clear Timer1 interrupt flag.
		STA		v1ier			; Enable interrupt from Timeout, and
		RTS						; return.

timoff	LDAIM	&C0				; Load Accumulator with 192.
		STA		v1ifr			; Clear Timer1 interrupt.
		LDAIM	&40				; Load Accumulator with 64.
		STA		v1ier			; Disable interrupt from Timeout, and
		RTS						; return.

psswrd	=		&7D				; 3
		=		&BD				; 2
		=		&77				; 9
		=		&77				; 9
		=		&DD				; 1

stored	=		&BD				; 2
		=		&B7				; 8
		=		&7B				; 6
		=		&DB				; 4
		=		&BB				; 5

		=		&00
		=		" (C) 1987, 2026 by Rich Teer"

		LNK		bugos2			; Continue assembly with the vectors.
