*	Fuzzy Logic Inferrence Engine
*
** This is the second variation of this fuzzy engine. The first used
*  a macro and conditional assembly for entry of rules and MFs. It
*  also combined fuzzification with rule evaluation. This version
*  is more efficient because fuzzy ins are only calculated once.
*  Also a c program was written to simplify rule and MF entry rather
*  than a macro and conditional assembly which were not very
*  portable from one assembler to another. This version uses fixed
*  data structures for 4 ins and 2 outs but rule format allows easy
*  expansion to 8 ins & 4 outs and could be expanded further.
*
*** Data structures and variables
*
            ORG     $0000   ;Beginning of HC11 RAM
CURRENT_INS RMB     4       ;Storage for 4 8-bit inputs
FUZ_INS     RMB     32      ;Storage for fuzzy inputs
FUZ_OUTS    RMB     16      ;Storage for fuzzy outputs
COG_OUTS    RMB     2       ;Defuzzified outputs
SUM_OF_FUZ  RMB     2       ;11-bit sum of fuzzy outs
SUM_OF_PROD RMB     3       ;19-bit sum of products
COGDEX      RMB     1       ;Current out # for COG loop
LP_COUNT    RMB     1       ;Index for fuz loop
SUMDEX      RMB     1       ;Index for sum loop

            ORG     $B600   ;Beginning of HC11 EEPROM
*
* Input membership functions are defined by four 8-bit values per
* input label. Up to 8 labels per input so data structure is
* 4*8*4=128 bytes.
* Membership functions are trapezoids with the base greater than or
* equal to the top. Values are entered into this program as four
* constants representing 2 points and 2 slopes. A separate c
* program is used to translate natural language into FCBs.
* 
*****

******************REPLACE THIS ENTIRE COMMENT BLOCK WITH OUTPUT***********
******************FILE FROM THE KNOWLEDGE BASE GENERATOR******************
*
* For now all MFs are filled with $FF. Use c tool to generate required FCBs
*
*INPUT_MFS   EQU     *                    ;Input membership functions
*
*IN0MF       EQU     *                    ;(2) TEMPERATURE
*            FCB     $FF,$FF,$FF,$FF      ;    (0) COLD
*            FCB     $FF,$FF,$FF,$FF      ;    (1) COOL
*            FCB     $FF,$FF,$FF,$FF      ;    (2) NORM
*            FCB     $FF,$FF,$FF,$FF      ;    (3) WARM
*            FCB     $FF,$FF,$FF,$FF      ;    (4) HOT
*            FCB     $FF,$FF,$FF,$FF      ;    (5) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (6) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (7) Unused
*
*IN1MF       EQU     *                    ;(1) EXAMPLE1
*            FCB     $FF,$FF,$FF,$FF      ;    (0) TEST1
*            FCB     $FF,$FF,$FF,$FF      ;    (1) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (2) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (3) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (4) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (5) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (6) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (7) Unused
*
*IN2MF       EQU     *                    ;(0) ROTATION
*            FCB     $FF,$FF,$FF,$FF      ;    (0) STOPPED
*            FCB     $FF,$FF,$FF,$FF      ;    (1) NOT_STOPPED
*            FCB     $FF,$FF,$FF,$FF      ;    (2) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (3) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (4) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (5) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (6) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (7) Unused
*
*IN3MF       EQU     *                    ;(3) DAYS_SINCE_RAIN
*            FCB     $FF,$FF,$FF,$FF      ;    (0) NONE
*            FCB     $FF,$FF,$FF,$FF      ;    (1) SHORT
*            FCB     $FF,$FF,$FF,$FF      ;    (2) MEDIUM
*            FCB     $FF,$FF,$FF,$FF      ;    (3) LONG
*            FCB     $FF,$FF,$FF,$FF      ;    (4) VERY_LONG
*            FCB     $FF,$FF,$FF,$FF      ;    (5) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (6) Unused
*            FCB     $FF,$FF,$FF,$FF      ;    (7) Unused
*
*****
*SGLTN_POS   EQU     *      ;Output singleton positions
*
*OUT0MF      EQU     *      ;(0) WATERING_TIME
*            FCB     $FF    ;    (0) CUT_OFF
*            FCB     $FF    ;    (1) DECREASE_GREATLY
*            FCB     $FF    ;    (2) DECREASE
*            FCB     $FF    ;    (3) NORMAL
*            FCB     $FF    ;    (4) INCREASE
*            FCB     $FF    ;    (5) INCREASE_GREATLY
*            FCB     $FF    ;    Unused
*            FCB     $FF    ;    Must fill 8 per output
*
*OUT1MF      FCB     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;Unused
*
*
* Each If part is of the form 000X XAAA where XX is an input (0-3)
* and AAA is a label (0-7) of that input. If parts are connected by
* ANDs. A rule may have any number of if parts (usually 2-4). Then
* parts are of the form 1000 YCCC where the MSB set indicates a
* then part, Y is the output number (0-1), and CCC is the output
* label (singleton 0-7). A rule may have any number of then parts
* (usually 1 or 2).
* A $FF indicates the end of a series of rules (number not limited).
*  
*	FCB	8*INx + LABa	If input XX is label AAA
*	FCB	$80+8*OUTy+LABc	Then output Y is label CCC
*
*RULE_START  EQU     *                   Start of first rule
* Example rule: If TEMPERATURE is VERY_HOT and DAYS_SINCE_RAIN is LONG
* Then WATERING_TIME is INCREASE_GREATLY
*RULE_1      FCB     (8*2)+4,(8*3)+3,$80+(8*0)+5
*END_OF_RULE FCB     $FF
*************************************************END OF REPLACED TEXT

***** Fuzzy Inferrence Engine Starts Here
*
FUZZIFY     LDY     #FUZ_INS      ;Point at fuzzy input table
            LDX     #CURRENT_INS  ;Point at current input values
            LDD     2,X           ;Put input values on stack
            PSHB
            PSHA
            LDD     0,X
            PSHB
            PSHA

            LDX     #INPUT_MFS    ;Point at input MF specs
NXTIN_LP    PULA                  ;Get next current input value
            LDAB    #7            ;For 8 loop passes
            STAB    LP_COUNT      ;Initialize loop counter
GRAD_LOOP   EQU     *             ;Get grade for 1 label of 1 input

*******************************************************************
* GET_GRADE - Routine to project a discrete input value onto      *
*   the associated input membership function (fuzzification).     *
*   Enter with Input value in A register and X pointing at the    *
*   points and slopes defining the membership function.           *
*   Finishes with grade in B, X points at next MF spec (X+4)      *
*   and A is unchanged.                                           *
*******************************************************************
GET_GRADE   PSHA                  ;Save input value of A
            CLRB                  ;In case grade = 0
            SUBA    2,X           ;Input value Ä pt2 -> A
            BLS     NOT_SEG2      ;If input < pt2
            LDAB    3,X           ;Slope 2
            BEQ     HAV_GRAD      ;Skip if vert slope	  
            MUL                   ;(In Ä pt1) * slp2 -> A:B
            TSTA                  ;Check for > $FF
            BEQ     NO_FIX        ;If upper 8 = 0
            CLRB                  ;Limit grade to 0
            BRA     HAV_GRAD      ;In limit region of seg 2
NO_FIX      SUBB    #$FF          ;B Ä $FF
            NEGB                  ;$FF Ä B
            BRA     HAV_GRAD      ;($FF Ä((In Ä pt2) * slp2))
NOT_SEG2    ADDA    2,X           ;Restore input value
            SUBA    0,X           ;Input value Ä pt1 -> A
            BLO     HAV_GRAD      ;In < pt1 so grade = 0
            LDAB    1,X           ;Slope 1
            BEQ     VERT_SLP      ;Skip if vert slope	  
            MUL                   ;(In Ä pt1) * slp1 -> A:B
            TSTA                  ;Check for > $FF
            BEQ     HAV_GRAD      ;Result OK in B
VERT_SLP    LDAB    #$FF          ;Limit region or vert slope
HAV_GRAD    INX                   ;Point at next MF spec
            INX
            INX
            INX
            PULA                  ;Restore A register

            STAB    0,Y           ;Save @ fuzzy input
            INY                   ;Point at next
            DEC     LP_COUNT      ;
            BPL     GRAD_LOOP     ;For 8 labels of 1 input
            CMPY    #FUZ_INS+32   ;Done with all fuzzy ins?
            BNE     NXTIN_LP      ;If not, process next input
* Done with fuzzification, evaluate rules next

            LDX     #FUZ_OUTS     ;Point at first fuzzy output
            LDAA    #16           ;16 fuzzy outputs
CLR_OUTS    CLR     0,X           ;Clear a fuzzy output
            INX                   ;Point at next
            DECA                  ;Loop index
            BNE     CLR_OUTS      ;Continue till all fuzzy outs 0
            LDY     #RULE_START   ;Point to start of 1st rule
RULE_TOP    LDAA    #$FF          ;Begin processing a rule string
* A will hold grade of smallest (min) if part
IF_LOOP     LDAB    0,Y           ;Get rule byte 000X XAAA; If X is A
            BMI     THEN_LOOP     ;If MSB=1, exit to then loop
            INY                   ;Point at next rule byte
            LDX     #FUZ_INS      ;Point at fuzzy inputs
            ABX                   ;Point to specific fuzzy input
            CMPA    0,X           ;Is this fuzzy input lower?
            BHS     IF_LOOP       ;If not don't replace lowest
            LDAA    0,X           ;Replace lowest if
            BNE     IF_LOOP       ;Unless zero, do next rule byte
* A zero value fuzzy input makes rule completely not true
* so skip rest of if parts and all then parts to start of next rule
FIND_THEN   LDAB    0,Y           ;Get next rule byte
            BMI     FIND_IF       ;MSB set means its a then part
            INY                   ;Point at next rule byte
            BRA     FIND_THEN     ;Loop till pointing at a then part
FIND_IF     INY                   ;Adv rule pointer to if part
            LDAB    0,Y           ;Get next rule byte
            BPL     RULE_TOP      ;MSB clear means its an if part
            CMPB    #$FF          ;$FF is no more rules marker
            BNE     FIND_IF       ;Continue looking for if or $FF
            BRA     DEFUZ         ;When all rules done, go defuzzify

THEN_LOOP   LDX     #FUZ_OUTS     ;Point at fuzzy outputs
            ANDB    #$0F          ;Save 8 times out # + label #
            ABX                   ;X points at fuzzy output
* Grade of membership for rule is in A accumulator
            CMPA    0,X           ;Compare to fuzzy output
            BLO     NOT_HIER      ;Branch if not higher
            STAA    0,X           ;Grade is higher so update
NOT_HIER    INY                   ;Adv rule pointer to next byte
            LDAB    0,Y           ;Get rule byte
            BPL     RULE_TOP      ;If MSB=0 its a new rule
CHK_END     CMPB    #$FF          ;Check for end of rules flag
            BNE     THEN_LOOP     ;If not $FF, must be a then part

* All rules evaluated. Now defuzzify fuzzy outputs	    
DEFUZ       LDY     #SGLTN_POS    ;Point at 1st output singleton
            LDX     #FUZ_OUTS     ;Point at 1st fuzzy output
            CLR     COGDEX        ;Loop index will run from 0->1
COG_LOOP    LDAB    #8            ;8 fuzzy outs per COG output
            STAB    SUMDEX        ;Inner loop runs 8->0
            LDD     #$0000        ;Used for quicker clears
            STD     SUM_OF_FUZ    ;Sum of fuzzy outputs
            STD     SUM_OF_PROD+1 ;Low 16-bits of sum of products
            STAA    SUM_OF_PROD   ;Upper 8-bits
SUM_LOOP    LDAB    0,X           ;Get a fuzzy output
            CLRA                  ;Clear upper 8-bits
            ADDD    SUM_OF_FUZ    ;Add to sum of fuzzy outputs
            STD     SUM_OF_FUZ    ;Update RAM variable
            LDAA    0,X           ;Get fuzzy output again
            LDAB    0,Y           ;Get Output singleton position
            MUL                   ;Position times weight
            ADDD    SUM_OF_PROD+1 ;Low 16-bits of sum of products
            STD     SUM_OF_PROD+1 ;Update low 16-bits
            LDAA    SUM_OF_PROD   ;Upper 8-bits
            ADCA    #0            ;Add carry from 16-bit add
            STAA    SUM_OF_PROD   ;Upper 8-bits of 24-bit sum
            INY                   ;Point at next singleton pos.
            INX                   ;Point at next fuzzy output
            DEC     SUMDEX        ;Inner loop index
            BNE     SUM_LOOP      ;For all labels this output
            PSHX                  ;Save index for now
            CLRA                  ;In case divide by zero
            LDX     SUM_OF_FUZ    ;Demominator for divide
            BEQ     SAV_OUT       ;Branch if denominator is 0
            TST     SUM_OF_PROD   ;See if more than 16-bit
            BNE     NUM_BIG       ;If not zero, # is > 16-bits
            LDD     SUM_OF_PROD+1 ;Numerator for divide
            IDIV                  ;Result in low 8-bits of X
            XGDX                  ;Result now in B
            TBA                   ;Move result to A
            BRA     SAV_OUT       ;Go save output
NUM_BIG     LDD     SUM_OF_PROD   ;Numerator upper 16 of 24-bit
            TST     SUM_OF_PROD+2 ;Check for rounding error
            BPL     NO_ROUND      ;If MSB clear, don't round
            ADDD    #1            ;Round numerator up 1
NO_ROUND    FDIV                  ;D/X -> X, use upper 8 of 16
            XGDX                  ;Result now in A
SAV_OUT     LDX     #COG_OUTS     ;Point to 1st defuz output
            LDAB    COGDEX        ;Curent output number
            ABX                   ;Point to correct output
            STAA    0,X           ;Update defuzzified output
            PULX                  ;Recover index
            INCB                  ;Increment loop index
            STAB    COGDEX        ;Update
            CMPB    #2            ;Done with two outs?
            BNE     COG_LOOP      ;If not, continue loop
*****
* Inference engine has completed one pass of all rules.
*****

