KEYCARD     BIT    00H             ;用于标志是否有键按下
KEYS        EQU    5AH    ;记录键值
KEY1        EQU    5BH    ;记录行扫描时的值
KEY2        EQU    5CH    ;记录列扫描时的值
    ORG    0000H     ;程序开始
    AJMP    START
    ORG    30H      ;为避免占用中断向量区,主程序从30H开始
START:
    MOV    SP,#5FH     ;设置堆栈的初始地址
    MOV    P3,#0FH     ;P3,P2,P0的初始化
    MOV    P2,#00H
    MOV    P0,#0FFH
    MOV    KEYS,#00H    ;各键值清0
    MOV    KEY1,#00H
    MOV    KEY2,#00H
    CLR    KEYCARD     ;有按键标志清0
LOOP:           ;循环扫描键盘
    ACALL    KEY_CHECK
    JNB    KEYCARD,LOOP
    ACALL    KEY_VAL    ;如果有键按下,则确定是哪一个键按下
    AJMP    LOOP
KEY_CHECK:       ;看有没有键按下
    MOV    A,P3
    ANL    A,#0FH
    CJNE    A,#0FH,KEY_CER   ;如果读入的与写出的不相等,延时消抖
    CLR    KEYCARD
    RET
KEY_CER:       ;延时,再读入,看是否真的有键按下
    ACALL    DELAY1S
    MOV    A,P3
    ANL    A,#0FH
    CJNE    A,#0FH,KEY_CE
    CLR    KEYCARD
    RET
KEY_CE:
    SETB    KEYCARD        ;真的有键按下,则置标志位
    RET
KEY_VAL:       ;用于获得键值码
    MOV    P3,#0FH
    MOV    A,P3
    MOV    KEY1,A     ;行键值码
    ANL    A,#0FH
    CJNE    A,#0FH,KEY_M1   ;确定有键按下,则确定列键值码,否则退出
    AJMP    KEY_EXIT
KEY_M1:
    MOV    P3,#0F0H
    MOV    A,P3
    MOV    KEY2,A     ;列键值码
    ANL    A,#0F0H
    CJNE    A,#0F0H,KEY_M2   ;如果又确定到有列键值码,则查表获得键值
    AJMP    KEY_EXIT
KEY_M2:
    MOV    A,KEY2
    MOV    R0,#KEY1
    XCHD    A,@R0
    MOV    KEYS,A
    MOV    R0,#00H
DISPLAY:
    MOV    A,R0
    INC    R0
    MOV    DPTR,#KEY_VALUE          ;根据键码查键值
    MOVC    A,@A+DPTR
    CJNE    A,KEYS,DISPLAY          ;直到查到对应的键值
    DEC    R0
    MOV    A,R0      ;把键值赋值给R0
    MOV    DPTR,#TAB
    MOVC    A,@A+DPTR
    MOV    KEYS,A
 MOV    P2,#10H     ;选通P2.4准备显示
 MOV    P0,A      ;把显示码送到P0
 ACALL    DELAY1S
KEY_EXIT:       ;用于退出键盘扫描
    CLR    KEYCARD
    RET
DELAY1S:       ;延时函数
    MOV    R7,#10
D1:
    MOV    R6,#50
    DJNZ    R6,$
    DJNZ    R7,D1
    RET
KEY_VALUE:                          ;键值表
 DB 0EBH,0DBH,0BBH,7BH,0E7H,0D7H,0B7H,77H
TAB:                                ;显示码
    DB    0xEF,0xDF,0xBF,0x7F,0xFE,0xFD,0xFB,0xF7
    END