Today, personal Safes are revolutionary locking storage cases that open with just the touch of your finger. These products are designed as secure storage for medications, jewelry, weapons, documents, and other valuable or
potentially harmful items.These utilize fingerprint recognition technology to allow access to only those whose fingerprints you choose. It contains all the necessary electronics to allow you to store, delete, and verify fingerprints with just the touch of a button. Stored fingerprints are retained even in the event of complete power failure or battery drain.These eliminates the need for keeping track of keys or remembering a combination password, or PIN. It can only be opened when an authorized user is present, since there are no keys or combinations to be copied or stolen, or locks that can be picked.
In this project the fingerprint module from Miaxis Biometrics is used. It can store up to 750 finger prints on its own memory. It can be controlled through its serial port.
The microcontroller AT89S52 interact with the module. You can Add a fingerprint, Delete a fingerprint and Identify the fingerprint.
To add a fingerprint, just show the finger on the module and press the ADD key. Now the microcontroller will send the ADD command to the module and the module will add it into the memory.
To delete the finger follow the same as above.
To identify the finger, press the Identify button and if the finger matches then the Relay is complemented.Also the fingerprint ID is displayed over the LCD display.
For more details about the Fingerprint module, please check the datasheet and you can get it from the datasheets page.
Circuit Diagram:
Code
RB0 EQU 00H ; Select Register Bank 0
RB1 EQU 08H ; Select Register Bank 1 ...poke to PSW to use
RB2 EQU 10H ; Select Register Bank 1 ...poke to PSW to use
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; PORT DECLERATION
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; ***LCD CONTROL***
LCD_RS EQU P0.0 ;LCD REGISTER SELECT LINE
LCD_E EQU P0.1 ;LCD ENABLE LINE
LCD_DB4 EQU P0.2 ;PORT 1 IS USED FOR DATA
LCD_DB5 EQU P0.3 ;USED FOR DATA
LCD_DB6 EQU P0.4 ;FOR DATA
LCD_DB7 EQU P0.5 ;FOR DATA
SEARCH EQU P1.0
ADDS EQU P1.1
DELETE EQU P1.2
LOAD EQU P0.7
ALARM EQU P0.6
; ***CURSOR CONTROL INSTRUCTIONS***
OFFCUR EQU 0CH
BLINKCUR EQU 0DH
; ***DISPLAY CONTROL INSTRUCTIONS***
CLRDSP EQU 01H
ONDSP EQU 0CH
; ***SYSTEM INSTRUCTIONS***
CONFIG EQU 28H ; 4-BIT DATA,2 LINES,5X7 MATRIX LCD
ENTRYMODE EQU 6 ; INCREMENT CURSOR DON'T SHIFT DISPLAY
DSEG ; This is internal data memory
ORG 20H ; Bit adressable memory
FLAGS1: DS 1
RECEIVED BIT FLAGS1.0
COUNTER: DS 1
BYTE: DS 10
TEMP: DS 1
TEMPS: DS 1
USER_COUNT: DS 1
ERROR_COUNT: DS 1
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CSEG ; Code begins here
; ---------==========----------==========---------=========---------
; Main routine. Program execution starts here.
; ---------==========----------==========---------=========---------
ORG 00H ; Reset
AJMP MAIN
ORG 23H
JMP SERIAL
; ---------==========----------==========---------=========---------
MAIN:
MOV PSW,#RB0 ; Select register bank 0
MOV SP,#60h
MOV A,PCON
SETB ACC.7
MOV PCON,A
MOV TMOD,#20H
MOV TH1,#0FFH
MOV SCON,#50H
SETB ES
SETB EA
SETB TR1
SETB ADDS
SETB SEARCH
CLR LOAD
CLR ALARM
MOV ERROR_COUNT,#00H
;CHECK TO INITIALIZE THE USER COUNT IN FLASH MEMORY
CLR RECEIVED
MOV DPTR,#READ_FLASH
CALL SEND_SERIAL
CALL DELAY
MOV A,BYTE+4
CJNE A,#0FFH,NOT_INT
MOV DPTR,#STORE_FLASH
CALL SEND_SERIAL
NOT_INT:
CALL RESETLCD4
TOPS: CALL DISPLAY
TOP:
JNB ADDS,ADD_USERS
JNB SEARCH,SEARCH_USERS
JNB DELETE,DELETE_USER
AJMP TOP
SEARCH_USERS:
AJMP SEARCH_USER
ADD_USERS:
AJMP ADD_USER
DELETE_USER:
JNB DELETE,$
CALL DISPLAY_DEL
CLR RECEIVED
MOV DPTR,#SEARCH_USER_DATA
CALL SEND_SERIAL
CALL DELAYS
MOV A,BYTE+4
CJNE A,#39H,NOT_MATCHS
CALL FIN_MATCHED
MOV TEMP,BYTE+6
CALL DELAYS
CALL DELAYS
MOV DPTR,#DEL_USER_DATA
MOV TEMPS,#00H
CALL SEND_SERIAL
MOV SBUF,TEMP
CALL TRANSDELAY
MOV A,TEMPS
ADD A,TEMP
MOV SBUF,A
CALL TRANSDELAY
CALL DELAYS
MOV A,BYTE+4
CJNE A,#31H,NOT_MATCHES
CALL FIN_DELETED
CALL DELAYS
CALL DELAYS
AJMP TOPS
NOT_MATCHES:
CALL NOT_DELETED
CALL DELAYS
CALL DELAYS
AJMP TOPS
NOT_MATCHS:
CALL NOT_MATCHED
CALL DELAYS
CALL DELAYS
AJMP TOPS
ADD_USER:
CALL DISPLAY1
CALL DELAYS
JNB ADDS,$
CLR RECEIVED
MOV DPTR,#SEARCH_USER_DATA
CALL SEND_SERIAL
CALL DELAYS
MOV A,BYTE+4
CJNE A,#39H,NOT_MATCHSS
CALL ALREADY_EXIT
CALL DELAYS
CALL DELAYS
AJMP TOPS
NOT_MATCHSS:
CLR RECEIVED
MOV DPTR,#READ_FLASH ;LOAD USER COUNT FROM FLASH MEMORY
CALL SEND_SERIAL
CALL DELAY
MOV A,BYTE+4
MOV USER_COUNT,A
CJNE A,#16,USER_NOT_FULL
CALL USER_FULL_DISPLAY
CALL DELAYS
CALL DELAYS
AJMP TOPS
USER_NOT_FULL:
CLR RECEIVED
MOV DPTR,#ADD_USER_DATA
MOV TEMPS,#00H
CALL SEND_SERIAL
MOV SBUF,USER_COUNT
CALL TRANSDELAY
MOV A,TEMPS
ADD A,USER_COUNT
MOV SBUF,A
CALL TRANSDELAY
NEXT:
CALL DELAYS
CALL DELAYS
JNB RECEIVED,$
MOV A,BYTE+4
CJNE A,#31H,NOT_SAVED
CALL DISPLAY_SUCESS
CALL DELAYS
CALL DELAYS
CLR RECEIVED
MOV DPTR,#STORE_FLASH
MOV TEMPS,#00H
CALL SEND_SERIAL
INC USER_COUNT
MOV SBUF,USER_COUNT
CALL TRANSDELAY
MOV A,TEMPS
ADD A,USER_COUNT
MOV SBUF,A
CALL TRANSDELAY
CLR RECEIVED
AJMP TOPS
NOT_SAVED:
CALL DISPLAY_NOTSUCESS
CALL DELAYS
CALL DELAYS
CLR RECEIVED
AJMP TOPS
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
SEND_SERIAL:
CLR A
MOVC A,@A+DPTR
CJNE A,#0FFH,SEND_D
RET
SEND_D:
MOV SBUF,A
ADD A, TEMPS
MOV TEMPS,A
CALL TRANSDELAY
INC DPTR
AJMP SEND_SERIAL
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
SEARCH_USER:
JNB SEARCH,$
CALL DISPLAY2
CLR RECEIVED
MOV DPTR,#SEARCH_USER_DATA
CALL SEND_SERIAL
CALL DELAYS
MOV A,BYTE+4
CJNE A,#39H,NOT_MATCH
CPL LOAD
CLR ALARM
MOV ERROR_COUNT,#00H
CALL MATCHED
MOV TEMP,BYTE+6
CALL SPLITDISP
CALL DELAYS
CALL DELAYS
AJMP TOPS
NOT_MATCH:
INC ERROR_COUNT
MOV A,ERROR_COUNT
CJNE A,#04H,ALARM_ON
SETB ALARM
ALARM_ON:
CALL NOT_MATCHED
CALL DELAYS
CALL DELAYS
AJMP TOPS
SPLITDISP:
MOV A,TEMP
mov b,#10 ; Divide By 10
div ab ; Do Divide
swap a ; Move Result To High Of A
orl a,b ; OR In Remainder
MOV TEMP,A
ANL A,#0F0H
SWAP A
ADD A,#30H
MOV R4,A
CALL WRLCDDATA
CALL MDELAY
MOV A,TEMP
ANL A,#0FH
ADD A,#30H
MOV R4,A
CALL WRLCDDATA
CALL MDELAY
MOV R4,#' '
CALL WRLCDDATA
CALL MDELAY
RET
;---------==========----------==========---------=========---------
ADD_USER_DATA:
DB 4DH, 58H, 10H, 03H, 40H, 00H, 0FFH
DB 4DH, 58H, 10H, 03H, 40H, 00H, 01H, 0F9H, 0FFH
;ADDRESS--------------------------^
SEARCH_USER_DATA:
DB 04DH, 58H, 10H, 05H, 44H, 00H, 00H, 00H, 0FEH, 0FCH, 0FFH
STORE_FLASH:
DB 4DH, 58H, 10H, 05H, 64H, 00H, 00H, 01H, 0FFH
DB 4DH, 58H, 10H, 05H, 64H, 00H, 00H, 01H, 00H, 1FH, 0FFH
READ_FLASH:
DB 4DH, 58H, 10H, 04H, 62H, 00H, 00H, 01H, 1CH, 0FFH
;ADDRESS--------------------------^
;COUNT---------------------------------^
DEL_USER_DATA:
DB 4DH, 58H, 10H, 03H, 42H, 00H, 0FFH
;---------==========----------==========---------=========---------
SERIAL:
PUSH PSW ; save current registerset
MOV PSW,#RB1
PUSH ACC
JB TI,TRANSD
MOV A,SBUF
CJNE A,#4DH,DOWN2
MOV COUNTER,#01H
JMP DDWN
TRANSD: AJMP TRANS
DOWN2:
MOV R1,COUNTER
CJNE R1,#01H,YH1
MOV BYTE,A
JMP DOWN1
YH1: CJNE R1,#02H,YH2
MOV BYTE+1,A
JMP DOWN1
YH2: CJNE R1,#03H,YH3
MOV BYTE+2,A
MOV TEMP,A
JMP DOWN1
YH3: CJNE R1,#04H,YH4
MOV BYTE+3,A
DEC TEMP
MOV A,TEMP
CJNE A,#00H,DOWN1
SETB RECEIVED
JMP DOWN1
YH4: CJNE R1,#05H,YH5
MOV BYTE+4,A
DEC TEMP
MOV A,TEMP
CJNE A,#00H,DOWN1
SETB RECEIVED
JMP DOWN1
YH5: CJNE R1,#06H,YH6
MOV BYTE+5,A
DEC TEMP
MOV A,TEMP
CJNE A,#00H,DOWN1
SETB RECEIVED
JMP DOWN1
YH6: CJNE R1,#07H,YH7
MOV BYTE+6,A
DEC TEMP
MOV A,TEMP
CJNE A,#00H,DOWN1
SETB RECEIVED
JMP DOWN1
YH7: CJNE R1,#08H,DOWN1
MOV BYTE+7,A
DEC TEMP
MOV A,TEMP
CJNE A,#00H,DOWN1
SETB RECEIVED
JMP DOWN1
DOWN1:INC COUNTER
DDWN: CLR RI
POP ACC
POP PSW
RETI
TRANS: CLR TI
POP ACC
POP PSW
RETI
;**********************************************************
;**********************************************************
TRANSDELAY:
MOV R7,#5FH
DJNZ R7,$
RET
;**************************************************************************
;##########################################################
; DISPLAY ROUTINES
;##########################################################
DISPLAY:
MOV DPTR,#MSAG1
CALL LCD_MSG
RET
MSAG1:
DB 1H,80H,'Fingerprint Based',0C0H,'Security System',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DISPLAY1:
MOV DPTR,#MSAG2
CALL LCD_MSG
RET
MSAG2:
DB 1H,83H,'Show your',0C0H,'Finger to ADD..',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DISPLAY2:
MOV DPTR,#MSAG3
CALL LCD_MSG
RET
MSAG3:
DB 1H,83H,'Show your',0C3H,'Finger....',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
USER_FULL_DISPLAY:
MOV DPTR,#MSAG4
CALL LCD_MSG
RET
MSAG4:
DB 1H,83H,'User memory',0C3H,'## FULL ##',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DISPLAY_SUCESS:
MOV DPTR,#MSAG5
CALL LCD_MSG
RET
MSAG5:
DB 1H,83H,'User Added',0C3H,'Sucessfully',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DISPLAY_NOTSUCESS:
MOV DPTR,#MSAG6
CALL LCD_MSG
RET
MSAG6:
DB 1H,83H,'User Added',0C2H,'## Failed ##',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MATCHED:
MOV DPTR,#MSAG7
CALL LCD_MSG
RET
MSAG7:
DB 1H,83H,'Fingerprint',0C1H,'Matched ID:',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOT_MATCHED:
MOV DPTR,#MSAG8
CALL LCD_MSG
RET
MSAG8:
DB 1H,83H,'Fingerprint',0C2H,'NOT Matched',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DISPLAY_DEL:
MOV DPTR,#MSAG9
CALL LCD_MSG
RET
MSAG9:
DB 1H,83H,'Show your',0C0H,'Finger to DELETE',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FIN_MATCHED:
MOV DPTR,#MSAG10
CALL LCD_MSG
RET
MSAG10:
DB 1H,83H,'Fingerprint',0C4H,'Matched',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FIN_DELETED:
MOV DPTR,#MSAG11
CALL LCD_MSG
RET
MSAG11:
DB 1H,83H,'Fingerprint',0C1H,'## Deleted ##',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOT_DELETED:
MOV DPTR,#MSAG12
CALL LCD_MSG
RET
MSAG12:
DB 1H,83H,'Fingerprint',0C2H,'NOT Deleted',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ALREADY_EXIT:
MOV DPTR,#MSAG13
CALL LCD_MSG
RET
MSAG13:
DB 1H,83H,'Fingerprint',0C1H,'Already Exits',00H
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DELAY:
MOV R1,#0FFH
RE1: MOV R2,#0FFH
RE: NOP
DJNZ R2,RE
DJNZ R1,RE1
RET
;**********************************************************
DELAYS: ;One second delay routine
MOV R0,#05H
RS3: MOV R1,#0FFH
RA1: MOV R2,#0FFH
RS2: NOP
DJNZ R2,RS2
DJNZ R1,RA1
DJNZ R0,RS3
RET
;**********************************************************
DELAYSS: ;One second delay routine
MOV R0,#03H
RE3: MOV R1,#0FFH
RZ1: MOV R2,#0FFH
RE2: NOP
DJNZ R2,RE2
DJNZ R1,RZ1
DJNZ R0,RE3
RET
;**********************************************************
; INITIALIZE THE LCD 4-BIT MODE
;**********************************************************
INITLCD4:
CLR LCD_RS ; LCD REGISTER SELECT LINE
CLR LCD_E ; ENABLE LINE
MOV R4, #CONFIG; FUNCTION SET - DATA BITS,
; LINES, FONTS
CALL WRLCDCOM4
MOV R4, #ONDSP ; DISPLAY ON
CALL WRLCDCOM4
MOV R4, #ENTRYMODE ; SET ENTRY MODE
CALL WRLCDCOM4 ; INCREMENT CURSOR RIGHT, NO SHIFT
MOV R4, #CLRDSP; CLEAR DISPLAY, HOME CURSOR
CALL WRLCDCOM4
RET
; **********************************************************
; SOFTWARE VERSION OF THE POWER ON RESET
; **********************************************************
RESETLCD4:
CLR LCD_RS ; LCD REGISTER SELECT LINE
CLR LCD_E ; ENABLE LINE
CLR LCD_DB7 ; SET BIT PATTERN FOR...
CLR LCD_DB6 ; ... POWER-ON-RESET
SETB LCD_DB5
SETB LCD_DB4
SETB LCD_E ; START ENABLE PULSE
CLR LCD_E ; END ENABLE PULSE
MOV A, #4 ; DELAY 4 MILLISECONDS
CALL MDELAY
SETB LCD_E ; START ENABLE PULSE
CLR LCD_E ; END ENABLE PULSE
MOV A, #1 ; DELAY 1 MILLISECOND
CALL MDELAY
SETB LCD_E ; START ENABLE PULSE
CLR LCD_E ; END ENABLE PULSE
MOV A, #1 ; DELAY 1 MILLISECOND
CALL MDELAY
CLR LCD_DB4 ; SPECIFY 4-BIT OPERATION
SETB LCD_E ; START ENABLE PULSE
CLR LCD_E ; END ENABLE PULSE
MOV A, #1 ; DELAY 1 MILLISECOND
CALL MDELAY
MOV R4, #CONFIG; FUNCTION SET
CALL WRLCDCOM4
MOV R4, #08H ; DISPLAY OFF
CALL WRLCDCOM4
MOV R4, #1 ; CLEAR DISPLAY, HOME CURSOR
CALL WRLCDCOM4
MOV R4,#ENTRYMODE ; SET ENTRY MODE
ACALL WRLCDCOM4
JMP INITLCD4
; **********************************************************
; SUB RECEIVES A COMMAND WORD TO THE LCD
; COMMAND MUST BE PLACED IN R4 BY CALLING PROGRAM
; **********************************************************
WRLCDCOM4:
CLR LCD_E
CLR LCD_RS ; SELECT READ COMMAND
PUSH ACC ; SAVE ACCUMULATOR
MOV A, R4 ; PUT DATA BYTE IN ACC
MOV C, ACC.4 ; LOAD HIGH NIBBLE ON DATA BUS
MOV LCD_DB4, C ; ONE BIT AT A TIME USING...
MOV C, ACC.5 ; BIT MOVE OPERATOINS
MOV LCD_DB5, C
MOV C, ACC.6
MOV LCD_DB6, C
MOV C, ACC.7
MOV LCD_DB7, C
SETB LCD_E ; PULSE THE ENABLE LINE
CLR LCD_E
MOV C, ACC.0 ; SIMILARLY, LOAD LOW NIBBLE
MOV LCD_DB4, C
MOV C, ACC.1
MOV LCD_DB5, C
MOV C, ACC.2
MOV LCD_DB6, C
MOV C, ACC.3
MOV LCD_DB7, C
CLR LCD_E
SETB LCD_E ; PULSE THE ENABLE LINE
CLR LCD_E
CALL MADELAY
POP ACC
RET
; **********************************************************
; SUB TO RECEIVE A DATA WORD TO THE LCD
; DATA MUST BE PLACED IN R4 BY CALLING PROGRAM
; **********************************************************
WRLCDDATA:
CLR LCD_E
SETB LCD_RS ; SELECT READ DATA
PUSH ACC ; SAVE ACCUMULATOR
MOV A, R4 ; PUT DATA BYTE IN ACC
MOV C, ACC.4 ; LOAD HIGH NIBBLE ON DATA BUS
MOV LCD_DB4, C ; ONE BIT AT A TIME USING...
MOV C, ACC.5 ; BIT MOVE OPERATOINS
MOV LCD_DB5, C
MOV C, ACC.6
MOV LCD_DB6, C
MOV C, ACC.7
MOV LCD_DB7, C
SETB LCD_E ; PULSE THE ENABLE LINE
CLR LCD_E
MOV C, ACC.0 ; SIMILARLY, LOAD LOW NIBBLE
MOV LCD_DB4, C
MOV C, ACC.1
MOV LCD_DB5, C
MOV C, ACC.2
MOV LCD_DB6, C
MOV C, ACC.3
MOV LCD_DB7, C
CLR LCD_E
SETB LCD_E ; PULSE THE ENABLE LINE
CLR LCD_E
NOP
NOP
POP ACC
RET
; **********************************************************
; SUB TAKES THE STRING IMMEDIATELY FOLLOWING THE CALL AND
; DISPLAYS ON THE LCD. STRING MUST BE TERMINATED WITH A
; NULL (0).
; **********************************************************
LCD_MSG:
CLR A ; Clear Index
MOVC A,@A+DPTR ; Get byte pointed by Dptr
INC DPTR ; Point to the next byte
JZ LCD_Msg9 ; Return if found the zero (end of stringz)
CJNE A,#01H,Lcd_Msg1 ; Check if is a Clear Command
MOV R4,A
CALL WRLCDCOM4 ;If yes, RECEIVE it as command to LCD
JMP LCD_MSG ;Go get next byte from stringz
Lcd_Msg1: CJNE A,#0FFH,FLL ;Check for displaying full character
MOV R4,A
CALL WRLCDDATA
JMP LCD_MSG
FLL: CJNE A,#080h,$+3 ; Data or Address? If => 80h then is address.
JC Lcd_Msg_Data ; Carry will be set if A < 80h (Data)
MOV R4,A
CALL WRLCDCOM4 ; Carry not set if A=>80, it is address
JMP Lcd_Msg ; Go get next byte from stringz
Lcd_Msg_Data: ;
MOV R4,A
CALL WRLCDDATA ; It was data, RECEIVE it to Lcd
JMP Lcd_Msg ; Go get next byte from stringz
Lcd_Msg9:
RET ; Return to Caller
; **********************************************************
; 1 MILLISECOND DELAY ROUTINE
; **********************************************************
MDELAY:
PUSH ACC
MOV A,#0A6H
MD_OLP:
INC A
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
JNZ MD_OLP
NOP
POP ACC
RET
MADELAY:
PUSH ACC
MOV A,#036H
MAD_OLP:
INC A
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
JNZ MAD_OLP
NOP
POP ACC
RET
END