* Fuzzy Logic Inferrence Engine
*
** This is the fifth 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. The 2nd version
* was 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. The 2nd version used fixed
* data structures for 4 ins and 2 outs. This, the third version, is
* identical to the 2nd version, except that the input and output size
* is variable. The number of Inputs and Outputs is set by the Knowledge
* Base Generator (kbg_11b1.exe). KBG_11B1 counts the actual number of
* Inputs and Outputs that you defined and generates an assembly language
* define statement for this Inference Engine to use.
*
* Revision: B2 - fourth variation
* Author : Raymond Carr (813) 461-6963
* Filename: FUZZ11B2.ASM
* Date : 06/30/92
* Notes: Errors were found in version Rev B1 which would not allow for
* more than 2 outputs. The reason for this was the mask used
* after the THEN_LOOP label was a #$0F. This mask is ANDed
* with the B accumulator to strip off the MSB indicating the
* THEN portion of the rule under evaluation. Since there are
* 8 singletons per output this allowed access to only 16 ($0F)
* singletons, hence 2 outputs. This mask was changed to #$7F to
* allow for up to 16 outputs. The KBG knowledge base generator
* will provide a valid rule base for only 4 outputs at this time.
*
* Revision B1 was designed to save memory by dynamically
* allocating memory via a EQU for the number of inputs, NUMINP,
* and the number of outputs, NUMOUT. The equations using these
* EQU's were enclosed in parenthesis, e.g. (8*NUMINP). The AS11
* assembler available on the FREEWARE BBS will evaluate this
* expression and return a result of 0. The equations were
* modified to take out the parenthesis and reorder the
* evaluation. AS11 will work with 8*NUMINP+FUZ_INS, but not
* FUZ_INS+8*NUMINP. Also, the dynamic allocation of inputs
* required a change that was not made in Rev B1. At the very
* beginning of the inference engine the program PUSHes the
* current actual inputs onto the stack starting with the last
* one. This works well because it allows the grading routine
* easy access to the next input by PULLing it off. However,
* the dynamic allocation of inputs requires dynamic PUSHing
* of the stack, otherwise every time you exit the routine
* your stack would be off by 4-(# of inputs). Dynamic allocation
* works now.
*
* Revision: B3 - fifth variation
* Author : Raymond Carr (813) 461-6963
* Filename: FUZZ11B3.ASM
* Date : 07/01/92
* Notes: A logic error was found in the rule evaluation when looking
* for the smallest component of an; IF .. IS ..; clause, the
* program was maxing all THEN outputs to $FF due to a
* BHS instead of a BLS 3 lines before the FIND_THEN label.
* Also, make sure if you are using the knowledge base generator
* routines you have a version later than kbg_11b2.exe. The
* earlier versions evaluate the rules incorrectly for this ASM
* file.
* An important observation is that if an output is not associated
* with a rule in the rule base whose IF .. IS.. clause provides
* some degree of membership the output value will be a 0. This
* is important if you have defined an output SPEED with
* singletons of HardReverse, MedReverse, SmallReverse,
* StandStill, SmallForward, MedForward, HardForward.
* HardReverse is associated with an actual binary value of 0 and
* StandStill is associated with a value of $7F. Therefore if
* SPEED is not associated with a rule whose IF .. IS.. clause is
* somewhat true it will get a value of 0, not $7F, your car will
* proceed backwards instead of standing still. i.e. HardReverse
* instead of StandStill. One way to overcome this problem is to
* provide a THEN clause for all defined outputs for every
* IF .. IS .. rule.
*** Data structures and variables
*
ORG $0000 ;Beginning of HC11 RAM
CURRENT_INS RMB NUMINP ;Storage for 8-bit inputs
FUZ_INS RMB 8*NUMINP ;Storage for fuzzy inputs (RIGHT NOW MAX=8)
FUZ_OUTS RMB 8*NUMOUT ;Storage for fuzzy outputs (RIGHT NOW MAX=4)
COG_OUTS RMB NUMOUT ;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
* Add the output from KBG_11C.exe here.....
***** Fuzzy Inferrence Engine Starts Here
* Program assumes at least 1 valid input
*
FUZZIFY LDY #FUZ_INS ;Point to fuzzy input table
LDX #CURRENT_INS+NUMINP-1 ;Point to last input value
PUSH_LOOP LDAA 0,X ;get value
PSHA ;and store on stack
CPX #CURRENT_INS ;last value?
BEQ DONE_PUSH ;if so, then branch
DEX ;else point to next byte
BRA PUSH_LOOP ;branch for next value
DONE_PUSH LDX #INPUT_MFS ;Point to 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 D pt2 -> A
BLS NOT_SEG2 ;If input < pt2
LDAB 3,X ;Slope 2
BEQ HAV_GRAD ;Skip if vert slope
MUL ;(In D 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 D $FF
NEGB ;$FF D B
BRA HAV_GRAD ;($FF D((In D pt2) * slp2))
NOT_SEG2 ADDA 2,X ;Restore input value
SUBA 0,X ;Input value D pt1 -> A
BLO HAV_GRAD ;In < pt1 so grade = 0
LDAB 1,X ;Slope 1
BEQ VERT_SLP ;Skip if vert slope
MUL ;(In D 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 #NUMINP*8+FUZ_INS ;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 #8*NUMOUT ;Number of 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?
BLS 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 #$7F ;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 #NUMOUT ;Done with outputs?
BNE COG_LOOP ;If not, continue loop
*****
* Inference engine has completed one pass of all rules.
*****