📄 计算器.txt
字号:
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 ; 指向下一个,继续判断
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
; =======================================================
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
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -