;*************************************************************
; CTEC 350 - Microcontroller Based Systems  |  Spring 2026
; Final Project : Safe Keypad System
; Instructor    : Dr. Duke
;
; Description:
;   A digital safe that accepts a 4-digit secret combination
;   entered on a 4x4 matrix keypad and shown on a 4-digit
;   multiplexed 7-segment display.  When the correct combo
;   is entered the display flashes "OPEn".  The '*' key
;   clears the entry at any time.
;
; Secret Combination: 3 - 5 - 7 - 9
;

;*************************************************************

;--- Symbolic names ---
SEGPORT EQU P2
DIGPORT EQU P3
KEYPORT EQU P1

DIG_IDX  EQU 34H
INP_CNT  EQU 35H
LAST_KEY EQU 36H
KEY_BUF  EQU 37H

;--- Secret combination (digit values 0-9) ---
S0 EQU 3
S1 EQU 5
S2 EQU 7
S3 EQU 9

;*************************************************************
;  Interrupt / Reset Vectors
;*************************************************************
    ORG 0000H
        LJMP MAIN

    ORG 000BH
        LJMP T0_ISR

;*************************************************************
;  MAIN
;*************************************************************
    ORG 0030H
MAIN:
    MOV  SP,  #60H

    MOV  P1,  #0FFH
    MOV  P2,  #00H
    MOV  P3,  #0FFH

    ACALL CLEAR_ALL

    MOV  TMOD, #01H
    MOV  TH0,  #0FCH
    MOV  TL0,  #18H
    SETB ET0
    SETB EA
    SETB TR0

;--- Main event loop ---
MAIN_LOOP:
    ACALL SCAN_KEY
    CJNE  A, #0FFH, KEY_ACTIVE
    MOV  LAST_KEY, #0FFH
    SJMP MAIN_LOOP

KEY_ACTIVE:
    MOV  R7, A
    CJNE  A, LAST_KEY, NEW_KEY_PRESS
    SJMP MAIN_LOOP

NEW_KEY_PRESS:
    MOV  LAST_KEY, R7

PROCESS_KEY:
    CJNE A, #12, NOT_CLEAR
    ACALL CLEAR_ALL
    SJMP  MAIN_LOOP

NOT_CLEAR:
    MOV  R7, A
    ACALL KEY_TO_DIGIT
    CJNE  A, #0FFH, VALID_DIGIT
    SJMP  MAIN_LOOP

VALID_DIGIT:
    MOV  R6, A
    MOV  A,  INP_CNT
    CJNE  A, #4, DO_STORE
    SJMP  MAIN_LOOP

DO_STORE:
    MOV  A, 31H
    MOV  30H, A
    MOV  A, 32H
    MOV  31H, A
    MOV  A, 33H
    MOV  32H, A
    MOV  33H, R6

    MOV  A,  INP_CNT
    ADD  A,  #KEY_BUF
    MOV  R0, A
    MOV  A,  R6
    MOV  @R0, A

    INC  INP_CNT

    MOV  A, INP_CNT
    CJNE  A, #4, MAIN_LOOP
    ACALL CHECK_COMBO
    SJMP  MAIN_LOOP

;*************************************************************
;  CHECK_COMBO
;*************************************************************
CHECK_COMBO:
    MOV  A, KEY_BUF+0
    CJNE  A, #S0, WRONG
    MOV  A, KEY_BUF+1
    CJNE  A, #S1, WRONG
    MOV  A, KEY_BUF+2
    CJNE  A, #S2, WRONG
    MOV  A, KEY_BUF+3
    CJNE  A, #S3, WRONG
    ACALL FLASH_OPEN
    ACALL CLEAR_ALL
    RET
WRONG:
    RET

;*************************************************************
;  FLASH_OPEN
;*************************************************************
FLASH_OPEN:
    MOV  R5, #6

FO_LOOP:
    MOV  30H, #10
    MOV  31H, #11
    MOV  32H, #12
    MOV  33H, #13
    ACALL DELAY_300
    MOV  30H, #0FFH
    MOV  31H, #0FFH
    MOV  32H, #0FFH
    MOV  33H, #0FFH
    ACALL DELAY_300
    DJNZ  R5, FO_LOOP
    RET

;*************************************************************
;  CLEAR_ALL
;*************************************************************
CLEAR_ALL:
    MOV  30H, #0FFH
    MOV  31H, #0FFH
    MOV  32H, #0FFH
    MOV  33H, #0FFH
    MOV  DIG_IDX,  #0
    MOV  INP_CNT,  #0
    MOV  LAST_KEY, #0FFH
    MOV  KEY_BUF+0, #0FFH
    MOV  KEY_BUF+1, #0FFH
    MOV  KEY_BUF+2, #0FFH
    MOV  KEY_BUF+3, #0FFH
    RET

;*************************************************************
;  SCAN_KEY
;*************************************************************
SCAN_KEY:
    MOV  DPTR, #ROW_MASK_TBL
    MOV  R4,   #0

SK_NEXT_ROW:
    MOV  A, R4
    MOVC A, @A+DPTR
    MOV  KEYPORT, A
    NOP
    NOP
    MOV  A, KEYPORT
    ANL  A, #0F0H
    CJNE  A, #0F0H, SK_HIT
    INC  R4
    CJNE  R4, #4, SK_NEXT_ROW
    MOV  A, #0FFH
    RET

SK_HIT:
    MOV  R3, A
    MOV  A,  R3
    ANL  A,  #10H
    JZ   SK_C0
    MOV  A,  R3
    ANL  A,  #20H
    JZ   SK_C1
    MOV  A,  R3
    ANL  A,  #40H
    JZ   SK_C2
    MOV  R1, #3
    SJMP SK_CALC
SK_C0: MOV  R1, #0
    SJMP SK_CALC
SK_C1: MOV  R1, #1
    SJMP SK_CALC
SK_C2: MOV  R1, #2

SK_CALC:
    MOV  A, R4
    MOV  B, #4
    MUL  AB
    ADD  A, R1
    MOV  R2, A

    CJNE  A, LAST_KEY, SK_NEW_KEY
    INC  R4
    CJNE  R4, #4, SK_NEXT_ROW
    MOV  A, #0FFH
    RET

SK_NEW_KEY:
    MOV  A, R2
    RET

ROW_MASK_TBL:
    DB  0FEH
    DB  0FDH
    DB  0FBH
    DB  0F7H

;*************************************************************
;  DEBOUNCE
;*************************************************************
DEBOUNCE:
    ACALL DELAY_20
    RET

;*************************************************************
;  KEY_TO_DIGIT
;*************************************************************
KEY_TO_DIGIT:
    MOV  DPTR, #KEY_MAP
    MOV  A,  R7
    MOVC A,  @A+DPTR
    RET

KEY_MAP:
    DB  01H, 02H, 03H, 0FFH, 04H, 05H, 06H, 0FFH
    DB  07H, 08H, 09H, 0FFH, 0FFH, 00H, 0FFH, 0FFH

;*************************************************************
;  T0_ISR
;*************************************************************
T0_ISR:
    PUSH ACC
    PUSH PSW

    MOV  TH0, #0FCH
    MOV  TL0, #18H

    MOV  DIGPORT, #0FFH

    MOV  A, DIG_IDX
    ADD  A, #30H
    MOV  R0, A
    MOV  A, @R0

    ACALL GET_SEG

    MOV  SEGPORT, A

    MOV  A,  DIG_IDX
    MOV  DPTR, #DIG_SEL_TBL
    MOVC A,   @A+DPTR
    MOV  DIGPORT, A

    MOV  A, DIG_IDX
    INC  A
    ANL  A, #03H
    MOV  DIG_IDX, A

    POP  PSW
    POP  ACC
    RETI

DIG_SEL_TBL:
    DB  0FEH
    DB  0FDH
    DB  0FBH
    DB  0F7H

;*************************************************************
;  GET_SEG
;*************************************************************
GET_SEG:
    CJNE  A, #0FFH, GS_LOOKUP
    MOV  A, #00H
    RET
GS_LOOKUP:
    MOV  DPTR, #SEG_TABLE
    MOVC A, @A+DPTR
    RET

SEG_TABLE:
    DB  3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H
    DB  7FH, 6FH, 3FH, 73H, 79H, 37H

;*************************************************************
;  DELAY_20
;*************************************************************
DELAY_20:
    MOV  R6, #3
D20_OUT:
    MOV  R7, #5
D20_IN:
    DJNZ R7, D20_IN
    DJNZ R6, D20_OUT
    RET

;*************************************************************
;  DELAY_300
;*************************************************************
DELAY_300:
    MOV  R5, #10
D300_LP:
    ACALL DELAY_20
    DJNZ  R5, D300_LP
    RET

    END
