最近两天一直在想可不可以用汇编做15位加法,带输入和输出的那种,经过调试,大致的思路如下。
有一个输入函数input,它接受从键盘输入的字符,并转换为数字存入内存;有一个输出函数output,它将已经转换过的结果输出;有一个加法函数plus,它实现的功能是把输入的两个数相加,并处理为ASCII码字符。
input、output以及main函数都相对简单,我认为难点主要是在plus函数这一块。
我刚开始想用两个指针分别指向两个数据,然后通过移动指针将数据相加,后来我发现这个方法有点麻烦(主要是不确定两个数据的长度是否一样,然后是否有进位);后来想到了另外一种方法,浮点数相加需要对阶,这里的加法也可以将短的数向右移(是整个数据右移,不是指移位操作),然后前面用0来补位,然后就方便处理了。
具体的代码如下:
extrn input:far,output:far,plus:far
public num1,num2,result
data segment
num1 db 16 dup(0) ; 存放已经经过转化的十进制数据
num2 db 16 dup(0)
result db 17 dup(0ffh),'$'
onceMore db 10,'Do you want to add your numbers once more(y/n): $'
data ends
code segment
assume cs:code,ds:data
start:
mov ax,data
mov ds,ax
;--------------------------------------------------------------------
mainProcess:
; 可能多次输入,所以需要将num1、num2和result重新初始化
mov si,0
initNum:
mov byte ptr [si],0
inc si
cmp si,20h
jne initNum
initResult:
mov byte ptr [si],0ffh
inc si
cmp si,31h
jne initResult
mov dl,10 ; 回车
mov ah,02h
int 21h
lea si,num1
call input ; inputd的入口参数是si 目的是将输入的字符串转换为数字并存入si指向的内存中去
cmp bl,0
je endJudge
mov di,si ; 首先保存第一次si的值
lea si,num2
call input ; inputd的入口参数是si 目的是将输入的字符串转换为数字并存入si指向的内存中去
cmp bl,0
je endJudge
call plus
call output
;--------------------------------------------------------------------
endJudge:
lea dx,onceMore
mov ah,09h
int 21h
mov ah,01h
int 21h
cmp al,'y'
je mainProcess
mov ax,4c00h
int 21h
code ends
end start
public input
data segment
inputError db 10,'Input data error!$'
inputMsg db 'Please input your data: $'
data ends
code segment
assume cs:code,ds:data
input proc far
mov ax,ds
mov es,ax ; 后面存数据的时候需要主程序的ds
push ds ; 保存主程序中的ds
mov ax,data
mov ds,ax
lea dx,inputMsg
mov ah,9h
int 21h
mov bl,1 ; bl为input函数是否成功标识,假设输入成功
mov cx,16
loopIn:
mov ah,01h
int 21h ; 调用 01h 号系统功能,输出为AL
cmp al,0dh ; 将al与回车比较
je endIn
cmp al,'0' ; 数据合法性检验
jb errorEnd
cmp al,'9'
ja errorEnd
; 数据合法,经过与'0'的减法操作,存入si指向的内存中
sub al,'0'
mov byte ptr es:[si],al
inc si
loop loopIn
errorEnd:
mov bl,0
lea dx,inputError
mov ah,9h
int 21h
endIn:
pop ds
ret
input endp
code ends
end
public plus
extrn result:byte,num1:byte,num2:byte
code segment
assume cs:code
movBigToX macro X,Y,Z
local ZBIGGER,endMov
mov X,0
cmp Y,Z
jle ZBIGGER
add X,Y
jmp endMov
ZBIGGER:
add X,Z
endMov:
endm
plus proc far
; di指向num1最尾部
; si指向num2最尾部
; 往result里面存数据时指针就跟si和di中较大者保持一致
sub si,10h
dec di
dec si
mov bl,0 ; bl为进位,由于在计算机内部使用16进制,必须处理
dec bp
; make Num Right Align
push si
push di ; 事先保存si和di的值
cmp si,di ; si和di相等 无需对齐数据
je preMyPlus
cmp si,di
jl alignNum2
alignNum1:
mov al,num1[di]
mov num1[si],al
dec si
dec di
cmp di,-1
je fillZeroNum1
jmp alignNum1
alignNum2: ; si < di
mov al,num2[si]
mov num2[di],al
dec si
dec di
cmp si,-1
je fillZeroNum2
jmp alignNum2
fillZeroNum1:
mov num1[si],0
dec si
cmp si,-1
jne fillZeroNum1
jmp preMyPlus
fillZeroNum2:
mov num2[di],0
dec di
cmp di,-1
jne fillZeroNum2
preMyPlus:
pop di
pop si
movBigToX bp,si,di ; si > di?(bp = si):(bp = di)
mov si,bp
mov di,bp
inc bp
myPlus:
; ds:[bp+result] = num1[di]+num2[si],si/di为-1怎么办,可以考虑先将短的数据右移,然后用0补位,这样运算要稍微简单一些
cmp si,-1
je plusOver
mov al,num1[di]
add al,num2[si]
add al,bl
cmp al,10d
jl noCarry
mov bl,1
sub al,10d
jmp giveValue
noCarry:
mov bl,0
giveValue:
mov byte ptr ds:[bp+result],al
dec si ; 调整指针
dec di
dec bp
jmp myPlus
plusOver:
mov al,0
add al,bl
mov byte ptr ds:[bp+result],al
; 将数字的结果转化为字符
mov si,0
numToChar:
cmp byte ptr result[si],0ffh
je printResult
add byte ptr result[si],'0'
inc si
jmp numToChar
printResult:
mov byte ptr result[si],'$'
ret
plus endp
code ends
end
public output
extrn result:byte
data segment
resultMsg db 'the add result is: $'
data ends
code segment
assume cs:code,ds:data
output proc far
mov ax,ds
mov es,ax ; 后面存数据的时候需要主程序的ds
push ds ; 保存主程序中的ds
mov ax,data
mov ds,ax
lea dx,resultMsg
mov ah,09h
int 21h
pop ds
lea dx,result
mov ah,09h
int 21h
ret
output endp
code ends
end
这四个文件需要分别编译,链接时以main为主。
最开始一直错一直错,后来发现我把汇编中的extrn写成了extern,还是不够细心啊……
这个程序只能够运算15位的加法,而且如果最前面没有进位的话,会多显示出来一个0。考虑到这个东西花了不少的时间了,就留待以后再改吧。