⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 calc.asm

📁 51计算器源代码,4*4键盘输入,采用浮点算法设计的
💻 ASM
📖 第 1 页 / 共 4 页
字号:
;; 显示程序符号定义
;;
        COM      EQU    45H
        DAT      EQU    46H
        CW_ADD   EQU  5FFCH
        CR_ADD   EQU  5FFDH
        DW_ADD   EQU  5FFEH
        DR_ADD   EQU  5FFFH


; 子程序说明:
; 寄存器工作组使用
; WAITKEY, 00
; KEYPRESSED, MAKENUM, RES2RAW 等子程序, 01
; FLOATING LIB, 10
; CALCULATE使用01, 当调用浮点程序库时 10
; 显示子程序,11
; ======================================================================
; 工作区使用注意:
; -=-= 1. 位寻址区 =-=-=-
; 20H-21H 两个字用作位状态(位地址00H-0FH)
; 23H-27H ; NOT USE! ESP. 23H
; 28H-2FH为显示区域与记录输入数字所用,RAWIN
       PNTB   BIT 10H ; 小数点位指示(位地址, POINT BIT)
       POSB   BIT 11H ; 位置位,POSB=1表示创建BCD低位,POSB=0表示创建BCD高位
       DPB    BIT 12H ; 大于1的浮点数MAKENUM时,指示小数点是否已经加入
; 04H-08H, ARBITRARY USE
       BYTE2  BIT 14H ; SEE BYTE2USAGE (CTRL+F, FIND IT.)


; STAT保存整个计算器的运行状态!
; BIT ASSIGNMENT:
;  .7: ERROR
;  .6: FLOAT
;  .5 & .4: OPERATOR
;         VALUE MEANING:
;               00: ADD
;               01: SUB
;               10: MUL
;               11: DIV
;  .3: OPERATOR PRESSED
;  .2: CONSTRUCTING NUM2
;  .1: CONSTRUCTING NUM1
;  .0: EQUAL SIGN PRESSED
       STAT   EQU 20H
       DCOUNT EQU 21H  ; 数字位数计数(DIGIT COUNT),只是RAWIN中有效字节
       INPUT  EQU 2AH  ; 键盘输入暂存
       RAWPTR EQU 2BH  ; 显示缓冲区指针
       NUMPTR EQU 2CH  ; 当前组建数字的指针
       TEMP1  EQU 2DH  ; 临时存储,一般用于临时保存R0, R1
       TEMP2  EQU 2EH  ; 以切换寄存器组, UNUNSED... 07.25.NIGHT

; -=-= 2. IRAM =-=-
; 30H-33H为第一个操作数
; 34H-37H为第二个操作数
; 38H-3FH为显示区域
; 48H-4FH为临时存储区域
; 结果则存储在第一个操作数位置
       NUM1    EQU 30H
       NUM2    EQU 34H
       RAWIN   EQU 38H
       TEMP    EQU 48H

; -=-=-= 3. REGISTERS =-=-=-
; 键盘扫描使用寄存器组 0,浮点程序使用寄存器组 2
; 其余数字组合部分用寄存器组 1
; 测试程序使用寄存器组 3

; ASCII TABLE
; '.' -> 2EH
; '+' -> 2BH
; '-' -> 2DH
; '*' -> 2AH
; '/' -> 2FH
; '=' -> 3DH

;/////////////////////////////////////////////////////////////////////
;///
;///             T H E  [ M A I N ]  P R O G R A M
;///                              ZEROX@2005.7.14
;////////////////////////////////////////////////////////////////////
           ORG 0000H
           LJMP MAIN
           ORG 0030H
MAIN:
                                ; 全局初始化
           MOV SP, #60H         ; 堆栈
           MOV IE, #00H         ; 禁止所有中断
                                ; 寄存器组 00
           CLR RS1
           CLR RS0
                                ; 工作区IRAM(20H-5FH)默认全为0
INIT20TO5F:
           MOV R0, #20H         ; START AT 20H
           MOV R7, #40H         ; 64 BYTES TO ZERO
LOOP20TO5F:
           MOV @R0, #00H
           INC R0
           DJNZ R7, LOOP20TO5F
           LJMP HCQ1
; ---------------------------------
           SETB STAT.0          ; 初始状态为等号状态
           MOV R7, #00H
           MOV SCON, #00H       ; 串行工作方式0
; -------------------------------------
                                ;DISPLAY INIT
; -------------------------------------
          ; LCALL LCDINIT
           MOV    COM,#06H
           ;LCALL  PR1
           MOV    COM,#0C0H
           ;LCALL  PR1

MAIN_LOOP:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;      键 盘 输 入
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                ; 获取键盘输入,使用寄存器组00
           CLR RS1
           CLR RS0
                                ; 获取输入
           MOV A, #0F0H
WAITKEY:
           NOP
           CJNE A, #0F0H, WAITKEY_OK
           LCALL KEY
           SJMP WAITKEY
WAITKEY_OK:
           MOV A, R7
           MOV INPUT, A         ; 保存键盘输入到INPUT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;         按键响应(内部处理)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                ; 处理键盘输入,使用寄存器组01
           CLR RS1
           SETB RS0
           LCALL KEYPRESSED

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;         显  示
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
           LCALL DISPLAY
           SJMP MAIN_LOOP

; ========================================================
; ==
; ==    S U B R O U T I N E S
; ==
; ========================================================
KEYPRESSED:
                                ; 键盘输入保存在 A 中,同时也保存在 INPUT 中
           ANL A, #0F0H         ; 屏闭低4位字节
           JNZ NONDIGIT         ; 高位非零,不是数字
; =========================================
                                ; 按键为数字
                                ; 否则按下的是数字,添加到显示缓冲区
           MOV A, INPUT         ; 取回数字,高位已经是0
                                ; 如果之前处于“等号”状态,则此为NUM1
           JB STAT.0, NEWNUM1
                                ; 如果之前处于运算符状态,则此为NUM2
           JB STAT.3, NEWNUM2
                                ; 如果处于第一个数字状态
           JB STAT.1, INNUM1
                                ; 如果处于第二个数字状态
           JB STAT.2, INNUM2
                                ; 否则出错!!

           SETB STAT.7          ; ERROR BIT
           RET                  ; KEYPRESSED 直接结束
; --------------------------
NEWNUM1:
           ANL STAT, #0F0H      ; 操作状态清零(低4位)
           SETB STAT.1          ; 计算器状态改为 NUM1
                                ; 同时清除浮点运算状态
           CLR STAT.6           ; STAT.6 -> FLOATING POINT
           SJMP NEWNUM
NEWNUM2:
           ANL STAT, #0F0H      ; 操作状态清零(低4位)
           SETB STAT.2          ; 计算器状态改为 NUM2
           SJMP NEWNUM
NEWNUM:
                                ; 准备开始一个新的操作数,首先清除显示缓冲区
           MOV RAWPTR, #RAWIN   ; 指向开始
                                ; 清除小数点标志位
           CLR PNTB
           MOV DCOUNT, #00H     ; 数字个数清零
                                ; 判断数字是否为0,0则忽略
           JZ IGNORE0
                                ; 非零数字,保存
           MOV RAWIN, A         ; 此时RAWPTR单元的值,也就是地址
                                ; 就是RAWIN
           INC RAWPTR
                                ; 数字位数增 1
           INC DCOUNT
IGNORE0:
           RET
; --------------------------
INNUM1:
INNUM2:
           MOV R2, DCOUNT
           CJNE R2, #08H, INNUM_OK
                                ; 数字个数已经达到最大值,忽略本次输入
           RET
INNUM_OK:
           MOV R0, RAWPTR       ; 使用R0间接寻址
           MOV @R0, A
           INC RAWPTR
           INC DCOUNT
           RET

; =========================================
                                ; 按键非数字
NONDIGIT:
           MOV A, INPUT         ; 恢复A
           INC A                ; '.' 用 0FFH 表示,加1后为0
           JZ DECPNT            ; DECIMAL POINT PRESSED
           DEC A                ; 否则不是小数点,减一恢复之

 ; 非数字的情况:
 ; 1. 加, 减, 乘, 除: 高4位应该分别为 0001, 0010, 0011, 0100
 ; 2. 小数点: 代码为 0FFH, 前面已经考虑
 ; 3. 等号: 1000 0000 (80H)

 ; 首先检查是否是等号 (最高位为1)
           JB ACC.7, KEYEQU

 ; 否则作为运算符对待
           LJMP KEYOP
; ---------------------------------

ERRORND:; ERROR OF NON-DIGIT
 ; 出错…………
           SETB STAT.7
           LJMP EXIT
; =======================================================
DECPNT:
 ; 根据计算器状态进行操作
           JB STAT.1, DP1
           JB STAT.2, DP2
           JB STAT.3, DP3
           JB STAT.0, DP0
 ; ERROR
           SETB STAT.7
           LCALL EXIT

DP1:
DP2:
 ; 在数字输入状态下下按下了小数点,如果之前已经按过小数点
 ; 则忽略此次输入,计算器状态字无需修改

           JB PNTB, DP_DONE ; 小数点已经按下
 ; 否则,这是本次操作数输入第一次按下小数点
 ; 首先设置小数点已经按下标志位
           SETB PNTB

 ; 否则添加小数点到输入区,也就是显示区
 ; 首先判断第一个数字是不是0
           MOV R0, RAWPTR ; 获取指针用于比较
 ; 如果当前输入位置不是开始(前面已有非零数字存储),直接添加小数点
           CJNE R0, #RAWIN, DP_ADD
 ; 否则,RAWPTR还是指向RAWIN位置,第一个数字设置为0,
 ; 后再添加小数点
           MOV R0, RAWPTR ; 用R0间接寻址
           MOV @R0, #00H
           INC RAWPTR
           INC DCOUNT
DP_ADD:
           MOV R0, RAWPTR ; R0间接寻址
           MOV @R0, #0FFH ; DECIMAL POINT
           INC RAWPTR
           INC DCOUNT
DP_DONE:
           RET
; --------------------
DP0:
 ; 之前的状态为等号,按下小数点后因该开始第一个操作数输入
 ; 设置状态
           ANL STAT, #0F0H
           SETB STAT.1
           SJMP DP_NEW
; --------------------
DP3:
 ; 之前处于操作符状态,按下小数点则应该开始第二个操作数输入
 ; 设置状态
           ANL STAT, #0F0H
           SETB STAT.2
 ; 新的操作数开始
           SJMP DP_NEW
; --------------------
DP_NEW:
 ; 小数点开始的新的操作数,添加'0' '.' 两个输入

 ; 初始化
           MOV RAWPTR, #RAWIN
           MOV DCOUNT, #00H
           CLR PNTB
           MOV RAWIN, #00H ; 第一个数字,0
           INC RAWPTR
           INC DCOUNT
           MOV R0, RAWPTR ; 使用R0间接寻址来存储小数点
           MOV @R0, #0FFH ; 小数点使用0FFH表示
           INC RAWPTR
           INC DCOUNT

           RET

; =======================================================

KEYEQU:
 ; 保存原来的状态
           MOV B, STAT
 ; 设置现在的状态为EQU
           ANL STAT, #0F0H ; 清楚低4位状态
           SETB STAT.0 ; EQU STAT BIT
 ; 根据原来的状态采取相应的操作
           JB B.1, EQU1 ; 原来处于第一个数的输入状态
           JB B.2, EQU2 ; 原来处于第二个数的输入状态 (NORMAL)
           JB B.3, EQU3 ; 原来处于操作符状态
           JB B.0, EQU0 ; 原来处于等号状态

 ; ERROR
           SETB STAT.7
           LJMP EXIT
EQU1:
 ; 在第一个数的状态下按了等号,那么第一个数不需要进行计算
 ; 直接作为结果显示,实际上RAWIN就是当前的操作数的显示格式
 ; 因此只需要将第一个数转换成浮点数存储于NUM1
           SETB RS0
           CLR RS1
           MOV R0, #RAWIN ; SOURCE
           MOV R1, #NUM1 ; DESTINATION
           LCALL MAKENUM ; NOTE: MAKENUM!!!
           RET
EQU2:
 ; 在第二个数的状态下按了等号,这是最普通的操作
 ; 首先转换操作数二到NUM2位置,然后计算结果
 ; 并将结果转化为RAWIN形式,供DISPLAY显示
           MOV R0, #RAWIN
           MOV R1, #NUM2 ; DESTINATION
           LCALL MAKENUM ; NOTE: MAKENUM..
 ; 计算结果,结果放在NUM1
           LCALL CALCULATE ; NOTE: CALCULATE!!!
 ; 结果转换为RAWIN,即从NUM1到RAWIN
           LCALL RES2RAW ; NOTE: RES2RAW
           NOP
           RET
EQU3:
 ; 原来状态为操作符,然后直接按了等号
 ; 本程序采取的措施为:等1号覆盖前面的操作符
 ; 因此不需要采取任何措施,直接同按下了第一个
 ; 操作数后直接按等号相同,由于按下操作符的时候
 ; 已经处理了第一个操作数,因此这里直接返回
           RET
EQU0: ; SYSTEM RESET
 ; 原来状态为等号,然后又按了等号
 ; 第一次按等号的时候已经处理好,这里只需返回
 ; 07.24 修改,连续两次等号相当于清0
 ; 首先清除错误状态位
           CLR STAT.7
           MOV DCOUNT, #00H ; DCOUNT设置为0,显示就为0
           RET

; ========================================================
; 操作符处理,注意:前面已经判断不是等号
KEYOP:
 ; 保存操作符号: 给定的是 10H, 20H, 30H, 40H
 ; 转换为 00H, 10H, 20H, 30H. 即,减去 10H
;DEBUGHERE
           CLR C
           SUBB A, #10H
 ; INVARIANT: 除了4, 5位,其他位不可能为1
KEYOP_NE: ; KEY OPERATOR, NO ERROR

 ; 如果是第一个数之后按的运算符
           JB STAT.1, KOP1
 ; 如果是第二个数之后按的运算符
           JB STAT.2, KOP2
 ; 如果是一个运算符之后按的运算符
           JB STAT.3, KOP3
 ; 如果是按了等号后按的运算符
           JB STAT.0, KOP0

 ; ERROR
           SETB STAT.7
           LJMP EXIT
; ------------------
KO_NE_DONE: ; KEY OPERATOR, NOT EQUAL, DONE
 ; 完成相应的操作后,更新STAT到当前状态
 ; 取回 INPUT ,并减去 10H ,成为STAT要求的操作符表示
           MOV A, INPUT
           CLR C
           SUBB A, #10H
 ; 运算符信息保存在 STAT 的 4,5位
 ; 先将4,5位清0
           ANL STAT, #0CFH  ; #1100 1111B
           ORL STAT, A  ; 设置4,5位
 ; 设置新的计算器状态
           ANL STAT, #0F0H ; 清除状态(低4位)
           SETB STAT.3 ; 操作符号状态
           RET  ; KEYPRESSED 返回
; --------
KOP1:
 ; 如果按了第一个数字之后按了操作符
 ; 则首先将当前显示缓冲区里的数字
 ; 拼合为3字节浮点数
 ; 保存在NUM1位置
 ; RAWIN 保持不变,因此计算器显示的仍然为
 ; 第一个操作数
           MOV R0, #RAWIN ; 需要进行拼凑的数字
           MOV R1, #NUM1 ; 目的
           LCALL MAKENUM
           SJMP KO_NE_DONE
KOP2:
 ; 如果按了第二个数字之后按了操作符,
 ; 首先计算前面的结果,然后结果作为
 ; 第一个数, 并设置状态为操作符
 ; 显示区域的数据为前面操作的结果
 ; 即:需要将3字节的浮点数结果转化
 ; 为可以显示的RAWIN格式。

 ; 创建第二个操作数
           MOV R0, #RAWIN
           MOV R1, #NUM2
           LCALL MAKENUM
 ; 执行计算
           LCALL CALCULATE
 ; 结果默认已经存储于到NUM1位置
 ; 但是需要将其转换为RAWIN形式用于DISPLAY子程序的显示
           LCALL RES2RAW ; NOTE: RES2RAW
           SJMP KO_NE_DONE
KOP3:
 ; 按了一个操作符后又按了另一个操作符,则直接忽略原来的
 ; 前面已经设置了操作符状态,此处无需进行任何操作
           SJMP KO_NE_DONE
KOP0:
 ; 按了等号之后按的操作符,则运算结果直接作为第一个
 ; 操作数,因为按等号的时候已经进行了结果运算和显示
 ; 所需要的准备工作(将浮点数转换为RAWIN形式)
 ; 因此这里不需要再作其他工作
           SJMP KO_NE_DONE

; =======================================================

EXIT:
           SJMP $

; =======================================================

; =======================================================
MAKENUM: ; NOTEE!!!
; [R0] 为RAWIN
; [R1] 为目标,NUM1 OR NUM2
; 先决条件:见前面
; 后决条件:NUM1中为二进制浮点数(3字节,补码形式)

; 转换结果,RAWIN形式的数字被转换为3字节二进制浮点数
          MOV A, R0
          MOV R3, A ; 保存源位置
          MOV A, R1
          MOV R4, A ; 保存目的位置
          MOV R2, #00H ; 小数点位置
 ; 保存 DCOUNT ,子程序结束后 DCOUNT
 ; 和 [R0] 内容应该不变
          PUSH DCOUNT

 ; 如果 DCOUNT 为零,则就是0
          MOV A, DCOUNT
          JZ VALZERO
          SJMP MN_DC_NZ ; DCOUNT NOT ZERO
; ------------------
; 结果为0
VALZERO:
 ; 三个字节均设置为0
          MOV @R1, #00H
          INC R1
          MOV @R1, #00H
          INC R1
          MOV @R1, #00H
          LJMP MAKENUM_DONE
; -----------------------

MN_DC_NZ: ; MAKENUM, DCOUNT, NOT ZERO
 ; 判断是否以0开始,是则为纯小数
          MOV A, @R0
          JZ PUREDEC

 ; 不是纯小数,则从第一位开始计算数字个数,
 ; 直到遇到小数点或者输入数据结束(即是个整数)
NONPUREDEC:
 ; 在计算指数的同时进行尾数的创建
          INC R1  ; 暂时忽略第一个指数字节

 ; POSB 为 0 则进行高字节创建
          CLR POSB
FINDPNT:
          MOV A, @R0 ; 取数
 ; 更新 R0 ,指向下一个
          INC R0
 ; 如果为小数点(用0FFH表示的一个字节),
 ; 则计数结束,否则继续
          CJNE A, #0FFH, NEXTFP

NDMAN_CONT: ; NON DECIMAL MANTISSA, CONTINUE
 ; 小数点找到,继续创建尾数的BCD码
 ; 指数已经保存于 R2
           DEC DCOUNT
           MOV A, DCOUNT
 ; 出口
           JZ NPD_DONE ; NON PURE DECIMAL DONE
           MOV A, @R0 ; 取数
           JB POSB, LOWER4_2 ; 创建低4位
; -------------
 ; 高4位
           SWAP A
           MOV @R1, A
           INC R0  ; 指向下一个,继续判断

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -