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

📄 calc.asm

📁 51计算器源代码,4*4键盘输入,采用浮点算法设计的
💻 ASM
📖 第 1 页 / 共 4 页
字号:
           SJMP NDMAN_CONT
; -------------
LOWER4_2:
 ; 低4位
           MOV B, @R1 ; 取回高位
           ORL A, B ; 整合为1个字节
           MOV @R1, A ; 保存回去
           CLR POSB ; 接下来是高4位
           INC R0  ; 下一个源
           INC R1  ; 下一个目的
           SJMP NDMAN_CONT
; ---------------------------------------
NEXTFP:
           INC R2 ; 整数个数加一

 ; 将这个数字组合为BCD码

           JB POSB, NUMLOWER4 ; 组合低位(POSB为1)?

 ; 组合高位,之后还要进行低位组合,不需要更新指针
           SWAP A
           MOV @R1, A
           SETB POSB  ; 高位已经组建
           SJMP NEXTFP1

NUMLOWER4:
 ; 取回已经组建了高字节的数据
           MOV B, @R1
 ; 加上低4位
           ORL A, B
 ; 再保存回去
           MOV @R1, A
 ; 更新目的指针
           INC R1
 ; 接下来组建新的字节的高位
           CLR POSB
NEXTFP1:
           DEC DCOUNT ; 输入数据计数减一
 ; 数据是否耗尽
           MOV A, DCOUNT
           JZ NPD_DONE ; 没有碰到小数点,是个整数
 ; 否则还有输入数据等待检验,继续
           SJMP FINDPNT

NPD_DONE: ; NON PURE DECIMAL DONE
 ; 数字组建结束,指数在R2中
           MOV A, R4
           MOV R1, A ; 恢复目的指针到初始值,以存放指数
           MOV A, R2 ; 取指数
           MOV @R1, A ; 保存在目的的第一个字节
           LJMP MAKENUM_DONE ; 跳到结束操作

; ----------------------------------------------------

PUREDEC: ; 纯小数的情况
 ; 计算小数点后0的个数,以求得指数
           INC R0 ; 指向小数点
 ; 相应的减少数字个数计数器,到零即表示数据处理完毕
           DEC DCOUNT
; MOVE FROM 07.25,001
COUNTZERO:
 INC R0  ; 下一个
 ; 取一个数就减少DCOUNT
 DEC DCOUNT ; MOVE TO, 07.25,001

 MOV A, @R0 ; 取出
 JZ PUREZERO ; 为零,计数

 ; 非零数字遇到,开始创建指数
 ; 0个数在R2中,移到A后取其补码
 MOV A, R2
 CPL A
 INC A  ; 取反加一即得到补码
 ; 最高位是符号位,为0
 CLR ACC.7
 ; 存储第一个字节
 MOV B, R4
 MOV R1, B
 MOV @R1, A
 INC R1  ; 指向尾数第一个字节

 ; 创建尾数
 ; R0 指向了尾数第一个数字
 ; 逐个读取只到读满4个数字或者输入数据用完(DCOUNT -> 0)

MANTISSA:
 ; BYTE2USAGE
 CLR BYTE2 ; 指示是否已经设置尾数中的第二个字节
   ; 因为可能在只有一位或者二位尾数的情况下,
   ; 尾数中的字节2没有设置,此时必须显示将其设为0
MAKEMAN:  ; MAKE MANTISSA
 ; 2字节的RAWIN对应于1个字节的BCD形式
 ; BCD高位:第一个数字
 MOV A, @R0 ; 取出
 SWAP A  ; 放到高字节
 MOV @R1, A ; 存放
 ; DCOUNT 等在子程序 UPDATEM 里更新
 ACALL UPDATE_MS ; UPDATE MANTISSA STATE
 ; BCD低位:第二个数字
 MOV B, @R1 ; 取回设置了高字节的BCD
 MOV A, @R0 ; 取输入数字
 ORL A, B ; 将原来的数和现在的数结合为BCD码
 MOV @R1, A
 ACALL UPDATE_MS ; 更新状态,判断是否结束

 ; 如果没有结束,开始一个新的字节
 ; 此时至少是第二个字节,设置 BYTE2
 SETB BYTE2
 ; 将所有的的输入数据都转换为BCD码。
 ; 虽然只有前4个字节(对应2字节BCD码)有效
 SJMP MAKEMAN

; -----------------------------------
UPDATE_MS:  ; UPDATE MANTISSA STATE
 INC R0
 DEC DCOUNT
 MOV A, DCOUNT
 JZ VALDONE ; 数字用完
 RET
VALDONE:
 ; 从UPDATEM子程序中跳出,首先跳过堆栈前面两个字节
 POP ACC
 POP ACC

 JB BYTE2, VALDONE_RET ; 第二个字节已经写入,可以返回
; 尾数第二个字节没有写过,说明还需要将末尾字节填0
; 例如,0.009,尾数为 90H XXH,第2个字节未设置
 MOV A, R4 ; 取目的地址
 MOV R1, A
 INC R1  ; 指向第3个字节,第2个字节
 INC R1  ; 不可能为零,否则整个数为0
 MOV @R1, #00H
VALDONE_RET:
 ; FINALIZE 操作
 LJMP MAKENUM_DONE

; -----------------------

; 计算小数点后0的个数
PUREZERO:

 INC R2  ; 0个数增加
; INC R1  ; 指向下一个数据 ; CMT. 07.25
; DEC DCOUNT ; DEL: 07.25
 MOV A, DCOUNT
 JZ TOVALZERO ; 如果在计算零的过程中数据用完,则这是个零
 SJMP COUNTZERO ; 继续计数
; -----------------------------------
TOVALZERO:
 LJMP VALZERO
; -----------------------------------
MAKENUM_DONE:
 POP DCOUNT ; 恢复 DCOUNT
 MOV A, R4
 ; 进行BCD到二进制浮点数的转换

 ; 使用寄存器工作组10 -> OBSOLETED. 07.25
 SETB RS1
 CLR RS0
 MOV R0, A ; R0指向刚刚创建的BCD浮点数
 LCALL BTOF ; BCD TO FLOAT
 ; 恢复寄存器工作组01
 CLR RS1
 SETB RS0
 RET  ; MAKENUM 完成

; =======================================================
CALCULATE: ; NOTEE!!!
 ; 操作数在NUM1, NUM2,格式为MAKENUM的结果,即二进制的浮点数
 ; 可以直接对其进行浮点程序库的调用
 ; REGISTER GROUP 10
 SETB RS1
 CLR RS0

 ; 操作数:
 MOV R0, #NUM1
 MOV R1, #NUM2

 ; 根据计算器的状态进行加减乘除的调用
; 操作符保存在 STAT 的第5第4位
;54: OPERATOR
;  00: ADD (0)
;  01: SUB (1)
;  10: MUL (2)
;  11: DIV (3)
 MOV A, STAT
 ANL A, #30H ; 仅保留5,4两位
 SWAP A  ; 切换到低位用于查表
 JZ CALC_ADD ; 0表示加
 DEC A
 JZ CALC_SUB ; 减1后为零,说明原来是1,表示减
 DEC A
 JZ CALC_MUL ; 乘法
 DEC A
 JZ CALC_DIV ; 除法
; -----------
CALC_DONE:
 ; 如果 OV 为1,则计算结果有误,设置错误位STAT.7
 ; 显示的时候如果STAT.7为1,则显示错误字符 E
 JNB OV, CALC_OK
 ; OV为1,设置 STAT.7
 SETB STAT.7
CALC_OK:
 RET
; --------
CALC_ADD:
 LCALL FADD
 SJMP CALC_DONE
CALC_SUB:
 LCALL FSUB
 SJMP CALC_DONE
CALC_MUL:
 LCALL FMUL
 SJMP CALC_DONE
CALC_DIV:
 LCALL FDIV
 SJMP CALC_DONE
HCQ00:
; =======================================================
RES2RAW: ; NOTEE!!!
 ; 运算结束后,NUM1中存放的是3字节二进制浮点数格式的结果,
 ; 将其转换为RAWIN形式存放于RAWIN位置,供显示程序使用

 ; 首先将结果转换为 BCD 的浮点数,保存于一个临时位置TEMP

 MOV R0, #NUM1 ; 初始化要转换的数字
 MOV R1, #TEMP ; 暂存地址

 MOV R2, #03H ; 移动3个字节, R2仅使用于R2R_COPY3
R2R_COPY3: ; 将NUM1复制到 TEMP
 MOV A, @R0
 MOV @R1, A
 INC R0
 INC R1
 DJNZ R2, R2R_COPY3

 MOV R0, #TEMP ; 将这个二进制浮点数转换为BCD格式浮点数
 ; 注意:NUM1中数字不可转换,因为之后还要在计算中使用

 ; 寄存器工作组 10
 SETB RS1
 CLR RS0
 LCALL FTOB ; FLOAT TO BCD

 ; 恢复使用寄存器组01
 CLR RS1
 SETB RS0

 ; 初始化
 MOV DCOUNT, #00H ; 数字个数计数(此计数包括减号和小数点)
 ; 判断是否为零(即2,3字节均为00H)
 MOV R0, #TEMP  ; 间接取址寄存器

 INC R0   ; 字节2
 MOV A, @R0
 JZ R2R_B2_ZERO  ; 为零,继续检查第三字节是否为零

 SJMP R2R_NORMAL  ; 开始正常处理

R2R_B2_ZERO: ; RES2RAW, BYTE 2, ZERO
 INC R0   ; 第三个字节
 MOV A, @R0
 JZ R2R_VAL_ZERO
 SJMP R2R_NORMAL  ; 开始正常处理
R2R_VAL_ZERO:  ; R2R, VALUE, ZERO
 MOV DCOUNT, #00H ; DCOUNT 为零表示结果为零
 RET
; ---------------
R2R_NORMAL:   ; 非零情况
 ; 寄存器使用:
 ; R0 源指针
 ; R1 目的指针
 ; R2 临时
 ; R3 临时
 ; R4
 ; R5
 ; R6 指数迭代器
 ; R7 循环计数器

 MOV R0, #TEMP  ; 源指针
 MOV R1, #RAWIN  ; 目的指针

 MOV A, TEMP  ; 取第一个字节,即指数
 JNB ACC.7, R2R_POS ; 最高位为0,正数

; 否则为负数,应该先放置一个'-'
 ; '-' 用 #0BH 表示
 MOV @R1, #0BH
 INC R1  ; 下一个位置
 INC DCOUNT ; DCOUNT指示整个字符串长度

R2R_POS: ; RES2RAW, POSITIVE: 正数
 MOV C, ACC.6 ; 指数的符号位是第7位
 MOV ACC.7, C ; 设置A的最高位为A的第6位,即指数的符号位
 JZ R2R_ZERO_E ; 指数为零,数字形式为 0.XXXX

 ; NOTE: JUMP OUT OF RANGE??
 JNB ACC.7, R2R_POS_E ; 指数为正

 ; 否则,指数为负,数字形式为 0.0..0XXX
 CPL A  ; 将A取反加1,就是绝对值
 INC A
 MOV R6, A ; 放入R6用于迭代
 SJMP R2R_PURE_DEC ; 纯小数
R2R_ZERO_E:  ; RES2RAW, ZERO EXPONENT, FORMAT IS 0.XXXX
 MOV R6, 0 ; 指数为0
 SJMP R2R_PURE_DEC ; 纯小数
; -------------------------
R2R_PURE_DEC:
 ; 根据R6中存放的指数(已经由二进制补码的负数转换为正数)在
 ; 小数点后面添加0

 ; 首先放置2个字符 '0' '.'
 MOV @R1, #00H ; '0'
 INC R1
 INC DCOUNT
 MOV @R1, #0FFH ; '.'
 INC R1
 INC DCOUNT
R2R_PAD_ZERO: ; 开始补0,根据R6值确定0的个数
 MOV A, R6
 ; 如果指数为零则结束
 JZ R2R_PAD_DONE
 ; 否则添加0
 MOV @R1, #00H
 INC R1
 INC DCOUNT ; DCOUNT 必须相应更新,包括'.' 和 '-' 的添加
 ; 更新指数信息
 DEC R6
 SJMP R2R_PAD_ZERO
; -----------
R2R_PAD_DONE: ; 纯小数小数点后的补零完成
 MOV R7, #02H ; R7为计数器,处理BCD浮点数的2个字节的尾数
R2R_EX_MAN:  ; RES2RAW, EXTRACT MANTISSA
 ; 开始尾数提取
 INC R0  ; 源地址指向第2个字节,即尾数
 MOV A, @R0 ; 取出
 ANL A, #0F0H ; 去掉低字节
 SWAP A  ; 高字节换到低字节
 MOV @R1, A ; 保存结果
 ; 更新目的状态
 INC R1
 INC DCOUNT

 MOV A, @R0 ; 再次取数
 ANL A, #0FH ; 屏蔽高字节
 MOV @R1, A ; 储存

 ; 更新目的状态
 INC R1
 INC DCOUNT

 DJNZ R7, R2R_EX_MAN ; 2个字节是否已完成?

 ; 纯小数的转换最终完成
 RET

; -------------------------
; RES2RAW, 指数为正的情况
; R6 中存放指数,当R6为零的时候,插入小数点
; 例如,23.45,指数为2,当提取了数字2和3,指数
; 迭代器减为0,应该插入小数点
; 例如,2345000,指数为7,当尾数提取完成,即2345时,
; 指数为3大于零,应该继续添加末尾的0,此时就没有小数点
R2R_POS_E:
 ; 小数点位,12.34的形式中,小数点添加后将此位置一
 CLR DPB  ; DECIMAL POINT BIT
 MOV R6, A ; A 中已经保存了去掉ACC.7位的指数
 INC R0  ; 源指针指向第2个字节,即尾数

; 字节1
 ; 高位
 MOV A, @R0 ; 读入高位字节
 ANL A, #0F0H
 SWAP A  ; 转换到低4位
 ACALL R2R_PUSHD

 ; 低位
 MOV A, @R0
 ANL A, #0FH ; 保留低4位
 ACALL R2R_PUSHD

; 字节2
 INC R0  ; 指向字节2
 ; 高位
 MOV A, @R0
 ANL A, #0F0H ; 保留高4位
 SWAP A  ; 转换到低4位
 ACALL R2R_PUSHD

 ; 低位
 MOV A, @R0 ; 重新读入
 ANL A, #0FH ; 保留低4位
 ; 第2个字节的低4位不需要判断指数是否为
 ; 0 来添加小数点,因为即使此时尾数变为0
 ; 也表示这个数是非零结尾的整数
 MOV @R1, A
 INC R1
 INC DCOUNT

; 这时还要判断指数是否还有,有则添加0 (EG. 12340000)
R2R_PE_JUDGE_E: ; RES2RAW, POSITIVE EXPONENT, JUDGE THE EXPONENT
 MOV A, R6
 JZ R2R_PE_DONE  ; R6 已经为0,返回
 ; 不为零
R2R_PE_PAD0: ; RES2RAW, POSITIVE EXPONETN, PAD ZERO
 ; 非零继续在末尾添加0
 MOV @R1, #00H
 INC R1
 INC DCOUNT
 DJNZ R6, R2R_PE_PAD0 ; 继续判断指数是否为0
; ----------------------------
R2R_PE_DONE:
 RET

; ------------------------
; 这是由 RES2RAW 调用的子程序
R2R_PUSHD: ; R2R, PUSH DIGIT
 ; 将 A 放入目的,并判断 R6 是否为0,是则加小数点
 MOV @R1, A
 INC R1
 INC DCOUNT
 ; 如果小数点已经写入,则R6已经为0,直接返回
 JB DPB, R2R_PD_DONE
 ; DECIMAL POINT?
 DJNZ R6, R2R_PD_DONE
 ; 指数变为0,应该添加小数点
 SETB DPB ; DECIMAL POINT BIT
 MOV @R1, #0FFH
 INC R1
 INC DCOUNT
R2R_PD_DONE: ; R2R, PUSH DIGIT, DONE
 RET
; ------------------------

; =======================================================
;*****************************************
;  KEY
;*****************************************
KEY:
           MOV DPTR,#2003H      ;0010 0000 0000 0011
           MOV A,#81H
           MOVX @DPTR,A
KEY0:
           MOV DPTR,#2001H
           CLR A
           MOVX @DPTR,A
           MOV DPTR,#2002H
           MOVX A,@DPTR
           CPL A
           ANL A,#0FH
           JZ KEY5
           MOV R6,#0FEH
           MOV R7,#00H
KEY1:
           MOV DPTR,#2001H
           MOV A,R6
           MOVX @DPTR,A
           MOV DPTR,#2002H
           MOVX A,@DPTR
           CPL A
           ANL A,#0FH
           JNZ KEY2
           MOV A,R7
           ADD A,#04H
           MOV R7,A
           MOV A,R6
           RL A
           MOV R6,A
           JB ACC.4,KEY1
           SJMP KEY
KEY2:
           RRC A
           JC KEY3
           INC R7
           SJMP KEY2
KEY3:
           MOV R6,#21H
           MOV DPTR,#2001H
           CLR A
           MOVX @DPTR,A
           MOV DPTR,#2002H
           MOVX A,@DPTR
           CPL A
           ANL A,#0FH
           JZ KEY4
           DJNZ R6,KEY3
KEY4:
           SETB P1.0
           MOV DPTR,#TBK
           MOV A,R7
           MOVC A,@A+DPTR
           MOV  R7,A
           SJMP KEY6
KEY5:
           MOV A,#0F0H
           NOP
KEY6:
           NOP
           RET
;---
TBK:
; 键盘布局
           DB 07H, 08H, 09H, 10H ; 7 8 9 +
           DB 04H, 05H, 06H, 20H ; 4 5 6 -
           DB 01H, 02H, 03H, 30H ; 1 2 3 *
           DB 00H, 0FFH, 80H, 40H ; 0 . = /

;**************************************
;显示程序
;**************************************
DISPLAY:
           MOV DPTR, #DISPTAB
           MOV R0, #RAWIN
           MOV TEMP1, DCOUNT
           ; CLEAR THE SCREEN
           MOV COM, #01H
           LCALL PR1
           MOV A, DCOUNT
           JZ DISP_ZERO
           JB STAT.7, DISP_ERR
DISP_NORMAL:

⌨️ 快捷键说明

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