;=====================================================================
;
;				LOGICMOD.ASM
;
;=====================================================================
;
; This microcode controls the pressure and forward-flow solenoids (or
; valves) for the hydraulic system in REKLAB. The state of the valves
; is determined by monitoring the external signals below and responding
; according to a designed state-flow diagram.
;
; External signals:
; -----------------
;   - Pump status
;   - Pressure On/Off push buttons
;   - Fwd Flow On/Off push buttons
;   - Panic button status
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; OSCILLATOR
; ----------
; An RC-oscillator is used to drive the microprocessor as frequency
; stability/accuracy is not an issue. The frequency need be high enough
; to give a timely response to user input (~ 100KHz should do).
;
; PORT INTERFACE
; --------------
; RA0: Forward Flow Valve control       o/p (0 = Off, 1 = On)
; RA1: Pressure valve control           o/p (0 = Off, 1 = On)
; RA2: LED Invalid Operation indicator  o/p
; RA3: N/C				o/p
; RA4: Heartbeat                        o/p (Toggles each loop pass)
;
; RB0: Pressure "On"  push button       i/p (Active low)
; RB1: Pressure "Off" push button       i/p (Active low)
; RB2: Forward-Flow "On"  push button   i/p (Active low)
; RB3: Forward-Flow "Off" push button   i/p (Active low)
; RB4: Pump status                      i/p (0 = Off, 1 = On)
; RB5: Panic button                     i/p (0 = OK,  1 = Panic)
; RB6: N/C				i/p
; RB7: N/C				i/p
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;                   (c) McGill University
;                       Biomedical Engineering Department
;		        Montreal, Quebec, Canada
;
; V1.0 MAY 21, 2001 - (Ross Wagner) Created file.
;
; V1.1 JUL 02, 2001 - (Ross Wagner) Added ability to reset state
; machine when in "invalid operation" mode by depressing both pressure
; and fwd-flow off buttons simultaneously.
;
;
;=====================================================================


;*********************************************************************
; CPU configuration
;*********************************************************************
; PIC16F84 microprocessor
; Code protection off, Power-up timer on, Watchdog timer off, 
; RC oscillator.

 processor 16f84
 include   <p16f84.inc>
 __config _CP_OFF & _PWRTE_ON & _WDT_OFF & _RC_OSC


;*********************************************************************
; EEPROM data
;*********************************************************************
; Store microcode version number on-chip.

	org	H'2100'
	de	"Logicmod V1.1", 0
	de	"2001-07-02",    0


;*********************************************************************
; Assembler constant declarations
;*********************************************************************
				; PORTA bit positions:
_FFVC	equ	0x00		; Fwd Flow valve control line
_PRVC	equ	0x01		; Pressure valve control line
_LEDC	equ	0x02		; LED status light control line
_HRTBT	equ	0x04		; Heartbeat monitor line

				; PORTB bit positions: 
_PRON	equ	0x00		; Pressure "On" button  (S' i/p)
_PROFF	equ	0x01		; Pressure "Off" button (R' i/p)
_FFON	equ	0x02		; Fwd Flow "On" button  (S' i/p)
_FFOFF	equ	0x03		; Fwd Flow "Off" button (R' i/p)
_PUMP	equ	0x04		; Pump status
_PANIC	equ	0x05		; Panic button

				; S'R' Latch bit positions:
_LRN	equ	0x00		; R'
_LSN	equ	0x01		; S'
_LQ	equ	0x02		; Q

				; Table index bit positions:
_TQ1	equ	0x05		; Q1
_TQ0	equ	0x04		; Q0
_TPUMP	equ	0x03		; Pump status
_TPR	equ	0x02		; Pressure state
_TFF	equ	0x01		; Fwd Flow state
_TPAN	equ	0x00		; Panic state

				; Table output bit positions:
_TQ1P	equ	0x07		; Q1+
_TQ0P	equ	0x06		; Q0+
_TLED	equ	0x05		; LED drive
_TPRV	equ	0x04		; Pressure Valve drive
_TFFV	equ	0x03		; Fwd Flow Valve drive


;*********************************************************************
; Macro definitions
;*********************************************************************

; Macro --- Bit-to-Byte conversion -----------------------------------
;
; Tests the bit (b) of a source file register (file) and sets the
; target file (byte) to either 0xFF (if bit is set) or 0x00 (if bit
; is clear).
;
;   file: file register
;   b:    bit number, 0 <= b <= 7
;   byte: file register
;
; Use this to setup a register for byte-oriented boolean operations.
; --------------------------------------------------------------------
b2byte	macro 	file, b, byte
	clrf	byte		; <byte> = 0x00
	btfsc	file, b		; if bit set then
	comf	byte, F		;   <byte> = 0xFF
	endm


; Macro --- Byte-to-Bit conversion -----------------------------------
;
; Clears the bit (b) of a destination file register (file) if source
; file register (byte) is 0x00 and otherwise sets the bit.
;
;   byte: file register
;   b:    bit number, 0 <= b <= 7
;   file: file register
; --------------------------------------------------------------------
byte2b	macro	byte, b, file
	bsf	file, b		; Set destination file bit
	movf	byte, F		; <byte> := <byte>
	btfsc	STATUS, Z	; if <byte> = 0x00
	bcf	file, b		;   then clear destination file bit
	endm


; Macro --- Bit transfer ---------------------------------------------
;
; Copies bit value of source file register, fsrc, at bit position bs to
; destination file register, fdest, at bit position bd.
;
;   fsrc:  file register
;   bs:    bit number, 0 <= bs <= 7
;   fdest: file register
;   db:    bit number, 0 <= bd <= 7
; --------------------------------------------------------------------
bxfr	macro	fsrc, bs, fdest, bd
	bsf	fdest, bd	; Set destination bit
	btfss	fsrc,  bs	; If source bit clear
	bcf	fdest, bd	;   then clear destination bit
	endm


; Macro --- Boolean AND Op -------------------------------------------
;
; Evaluates the boolean expression <out> = <arg1> .AND. <arg2>
;
;   arg1: file register
;   arg2: file register
;   out:  file register
;
; --------------------------------------------------------------------
and	macro	arg1, arg2, out
	movf	arg1, W		; w := <arg1>
	andwf	arg2, W		; w := w .AND. <arg2>
	movwf	out		; <out> := w
	endm


; Macro --- Boolean NAND Op ------------------------------------------
;
; Evaluates the boolean expression <out> = <arg1> .NAND. <arg2>
;
;   arg1: file register
;   arg2: file register
;   out:  file register
;
; --------------------------------------------------------------------
nand	macro	arg1, arg2, out
	and	arg1, arg2, out	; <out> = <arg1> .AND. <arg2>
	comf	out, F		; <out> = .NOT. <out>
	endm


; Macro --- Bit Complementation --------------------------------------
;
; Complements the b-th bit of a file register (arg). (Uses TEMP
; scratch variable.)
;
;   arg: file register
;   b:   bit number, 0 <= b <= 7
;
; --------------------------------------------------------------------
bcomf	macro	arg, b
	movf	arg, W		; w := <arg>
	movwf	TEMP		; TEMP := w
	comf	TEMP, F		; TEMP := .NOT. TEMP
	bxfr	TEMP, b, arg, b	; Transfer bit from TEMP to <arg>
	endm



;*********************************************************************
; Assign memory locations for variable storage
;*********************************************************************

PR_LATCH	equ	0x0C	; Pressure S'R' latch vector
FF_LATCH	equ	0x0D	; Forward-Flow S'R' latch vector
PORTA_BUF	equ	0x0E	; Storage for Port A values
PORTB_BUF	equ	0x0F	; Storage for port B values (latch)
MACHINE		equ	0x10	; Machine state vector (See Table:)

TEST		equ	0x48	; Scratch variable
TEMP		equ	0x49	; Scratch variable
INDEX		equ	0x4A	; Index into state-transition table

				; Variables needed for S'R' latch
LATCH		equ	0x4B	; - I/O arguments for SRlatch routine
SRQ		equ	0x4C	; - Internal variable  (Q  o/p)
SRQN		equ	0x4D	; - Internal variable  (Q' o/p)
SNOT		equ	0x4E	; - Internal variable  (S' i/p)
RNOT		equ	0x4F	; - Internal variable  (R' i/p)


;*********************************************************************
;*********************************************************************
;
; PROGRAM STARTS HERE
;
;*********************************************************************
;*********************************************************************

	org	0x00		; Set prog memory base at reset vector
	goto	Start

	org	0x04		; Set prog memory base at interrupt vector

Start:  
        ; At startup all ports are inputs.
	; Set port A for output on all pins.

	clrf	PORTA_BUF		; Clear output vector
	movf	PORTA_BUF, W		; and copy to output
	movwf	PORTA			; port latch.
	bsf	STATUS, RP0		; Select Bank 1
	movlw	B'00000000'		; Set all pins as o/p
	movwf	TRISA			; Apply configuration
					; Now config port B
	bcf	OPTION_REG, NOT_RBPU	; Enable internal pull-ups
	bcf	STATUS, RP0		; Select Bank 0
	bcf	INTCON, GIE		; Disable all interrupts

	; Port A is now putting out 0x00
	; To be consistent with this scenario both pressure and forward
	; flow must be off. Set their latch vectors accordingly.

	movlw	B'00000011'	; See SRlatch subroutine for details
	movwf	PR_LATCH	;
	movwf	FF_LATCH	;

	; Also, the machine state at the power-up is defined to be 00

	clrf	MACHINE

Loop:
	; See what is happening in the world...

	movf	PORTB, W	; Read PORTB, store in w
	movwf	PORTB_BUF	; Latch PORTB values

	; Pump status is actually using negative logic: A low signal
	; indicates that the pump is "On". Need to flip this bit in
	; order to be consistent with expectations of this program.
	bcomf	PORTB_BUF, _PUMP

	; Update the PRESSURE latch vector to register any push button
	; having been depressed during port sample.
	;
	; Transfer state of pressure S' bit of port input to appropriate
	; bit of pressure S'R' latch vector. Then do the same for the R'
	; input.

	bxfr	PORTB_BUF, _PRON,  PR_LATCH, _LSN
	bxfr	PORTB_BUF, _PROFF, PR_LATCH, _LRN

				; Compute resulting latch states.
	movf	PR_LATCH, W	; Need to copy PR_LATCH to i/o
	movwf	LATCH		; variable used by subroutine.
	call	SRlatch		; Get the latch states.
	movf	LATCH, W	; Store result in pressure latch vector.
	movwf	PR_LATCH	;

	; Update the FORWARD FLOW latch vector to register any push button
	; having been depressed during port sample.
	;
	; Transfer state of forward flow S' bit of port input to appropriate
	; bit of forward flow S'R' latch vector. Then do the same for the R'
	; input.

	bxfr	PORTB_BUF, _FFON,  FF_LATCH, _LSN
	bxfr	PORTB_BUF, _FFOFF, FF_LATCH, _LRN

				; Compute resulting latch states.
	movf	FF_LATCH, W	; Need to copy FF_LATCH to i/o
	movwf	LATCH		; variable used by subroutine.
	call	SRlatch		; Get the latch states.
	movf	LATCH, W	; Store result in fwd flow latch vector.
	movwf	FF_LATCH	;

	; Cross-reference state-transition table to determine next
	; state of the state machine.

	; Setup table index (See Table for details).
	clrf	INDEX
	bxfr	MACHINE,    _TQ1P, INDEX, _TQ1		; Q1 state
	bxfr	MACHINE,    _TQ0P, INDEX, _TQ0		; Q0 state 
	bxfr	PORTB_BUF,  _PUMP, INDEX, _TPUMP	; Pump status
	bxfr	PR_LATCH,     _LQ, INDEX, _TPR		; Pressure state
	bxfr	FF_LATCH,     _LQ, INDEX, _TFF		; Fwd Flow state
	bxfr	PORTB_BUF, _PANIC, INDEX, _TPAN		; Panic state

	movf	INDEX, W	; Prepare for table lookup.
	call	Table		; Do it.
	movwf	MACHINE		; Save result.

	; Check for over-riding reset condition. A reset places the
	; machine into the "power-up" mode and is allowed when the
	; machine is in the invalid-operation mode and both pressure
	; and forward-flow "off" buttons are depressed.

	clrf	TEST				; Setup test bits
	bxfr	MACHINE,   _TQ1P,  TEST, 2	; Q1 state
	bxfr	PORTB_BUF, _PROFF, TEST, 1	; Pressure off i/p
	bxfr	PORTB_BUF, _FFOFF, TEST, 0	; Fwd-flow off i/p

	movf	TEST, F		; if TEST = 0x00
	btfsc	STATUS, Z	; then reset condition present
	clrf	MACHINE		; reset machine state.

	; The resulting pressure and forward-flow valve drives need to
	; match the pressure and fwd-flow states stored in their
	; respective S'R' latches. When the machine states are directed
	; solely by the pressure and fwd-flow push buttons then this
	; happens automatically. Under all other circumstances there
	; will be a mismatch and the valve drives must update the
	; push-button latches. Hence the valve-drive states can
	; override the latch states without unwanted consquences.
	; Do that now.

	bxfr	MACHINE, _TPRV, PR_LATCH, _LQ
	bxfr	MACHINE, _TFFV, FF_LATCH, _LQ

	; Now ready to output to the world...
	; Setup output vector
	
	bxfr	MACHINE, _TLED, PORTA_BUF, _LEDC
	bxfr	MACHINE, _TPRV, PORTA_BUF, _PRVC
	bxfr	MACHINE, _TFFV, PORTA_BUF, _FFVC

	; Place a heartbeat on an o/p pin that toggles each time
	; through this loop. It gives the user a way of monitoring
	; program execution.

	bcomf	PORTA_BUF, _HRTBT

	; Now write to the world
	movf	PORTA_BUF, W
	movwf	PORTA

	goto	Loop



;*********************************************************************
; Subroutine and Table definitions
;*********************************************************************

SRlatch:
; --------------------------------------------------------------------
; Subroutine
; ==========
;
; Implements an S'R' latch (read "not" for ')
;
; Original latch state, current inputs, and resulting latch state
; are passed via file register LATCH with the following encoding
; scheme:
;
;   LATCH<2>: Current Q   state  (S' NAND gate o/p)
;   LATCH<1>: Current S'   i/p
;   LATCH<0>: Current R'   i/p
;
; Upon completion of this routine new latch sate will be returned in
; LATCH<2>. In the illegal input case of S' = 0 and R' = 0 the latch
; state remains unchanged.
; --------------------------------------------------------------------

	btfss	LATCH, _LSN	; Check latch inputs
	goto	chkr		; S' = 0 (trouble if also R' = 0)
	goto	srok

chkr:	btfss	LATCH, _LRN
	return			; R' = 0. Illegal. Do nothing.

srok:				; Inputs valid. Determine new latch state.

	; Extract LATCH bits into file registers for processing
	b2byte	LATCH, _LQ,  SRQ
	b2byte	LATCH, _LSN, SNOT
	b2byte	LATCH, _LRN, RNOT

	comf	SRQ, W		; Create Q' state
	movwf	SRQN		;

	; Evaluate the boolean equations for the latch.
	; Need 3 nand evals in order to handle case where
	; (S', R') goes from (0,1) to (1,0). Otherwise, last
	; statement is redundant.

	nand	SNOT, SRQN, SRQ		; Q  := (S' * Q')'
	nand	RNOT, SRQ,  SRQN	; Q' := (R' * Q )'
	nand	SNOT, SRQN, SRQ		; Q  := (S' * Q')'

	; Pack result back into LATCH vector
	byte2b	SRQ,  _LQ,  LATCH

	return

Table:
; --------------------------------------------------------------------
; State-transition table. Used to determine next machine state and 
; outputs based on current machine state and inputs.
;
; The offset into the table is supplied via the W register, coded as
; follows:
;
; Bit	Description
; -------------------------------
;  7	Must be 0
;  6	Must be 0
;  5	Current machine state (Q1)
;  4	Current machine state (Q0)
;  3	Pump status (0 = off, 1 = on)
;  2	Pressure state (0 = off, 1 = on)
;  1	Forward-flow state (0 = off, 1 = on)
;  0	Panic button state (0 = Normal Operation, 1 = Panic)
;
; Table output is returned in the W register, coded as follows:
;
; Bit	Description
; -------------------------------
;  7	Next machine state (Q1+)
;  6	Next machine state (Q0+)
;  5	LED status light (0 = OK, 1 = invalid operation)
;  4	Pressure valve drive (0 = off, 1 = on)
;  3	Forward-flow valve drive (0 = off, 1 = on)
;  2	Reserved
;  1	Reserved
;  0	Reserved
;
; --------------------------------------------------------------------
	addwf	PCL, F		; Add table index to PC to generate
				; a computed goto

	retlw	B'00000000'	; Index  0
	retlw	B'00000000'	; Index  1
	retlw	B'00000000'	; Index  2
	retlw	B'00000000'	; Index  3
	retlw	B'00000000'	; Index  4
	retlw	B'00000000'	; Index  5
	retlw	B'00000000'	; Index  6
	retlw	B'00000000'	; Index  7
	retlw	B'00000000'	; Index  8
	retlw	B'00000000'	; Index  9
	retlw	B'00000000'	; Index 10
	retlw	B'01100000'	; Index 11
	retlw	B'10010000'	; Index 12
	retlw	B'01100000'	; Index 13
	retlw	B'01100000'	; Index 14
	retlw	B'01100000'	; Index 15

	retlw	B'00000000'	; Index 16
	retlw	B'00000000'	; Index 17
	retlw	B'00000000'	; Index 18
	retlw	B'00000000'	; Index 19
	retlw	B'00000000'	; Index 20
	retlw	B'00000000'	; Index 21
	retlw	B'00000000'	; Index 22
	retlw	B'00000000'	; Index 23
	retlw	B'01100000'	; Index 24
	retlw	B'01100000'	; Index 25
	retlw	B'01100000'	; Index 26
	retlw	B'01100000'	; Index 27
	retlw	B'01100000'	; Index 28
	retlw	B'01100000'	; Index 29
	retlw	B'01100000'	; Index 30
	retlw	B'01100000'	; Index 31

	retlw	B'00000000'	; Index 32
	retlw	B'00000000'	; Index 33
	retlw	B'00000000'	; Index 34
	retlw	B'00000000'	; Index 35
	retlw	B'00000000'	; Index 36
	retlw	B'00000000'	; Index 37
	retlw	B'00000000'	; Index 38
	retlw	B'00000000'	; Index 39
	retlw	B'00000000'	; Index 40
	retlw	B'01100000'	; Index 41
	retlw	B'01100000'	; Index 42
	retlw	B'01100000'	; Index 43
	retlw	B'10010000'	; Index 44
	retlw	B'10010000'	; Index 45
	retlw	B'11011000'	; Index 46
	retlw	B'01100000'	; Index 47

	retlw	B'00000000'	; Index 48
	retlw	B'00000000'	; Index 49
	retlw	B'00000000'	; Index 50
	retlw	B'00000000'	; Index 51
	retlw	B'00000000'	; Index 52
	retlw	B'00000000'	; Index 53
	retlw	B'00000000'	; Index 54
	retlw	B'00000000'	; Index 55
	retlw	B'00000000'	; Index 56
	retlw	B'01100000'	; Index 57
	retlw	B'01100000'	; Index 58
	retlw	B'01100000'	; Index 59
	retlw	B'10010000'	; Index 60
	retlw	B'10010000'	; Index 61
	retlw	B'11011000'	; Index 62
	retlw	B'10010000'	; Index 63

 end	; of program
