192 lines
6.5 KiB
NASM
192 lines
6.5 KiB
NASM
;A simple A + B Program , using 8086 Assembler
|
||
;Copyright © 2022 Lucas & yydk77.cn
|
||
;
|
||
;Licensed under the Apache License, Version 2.0 (the "License");
|
||
;you may not use this file except in compliance with the License.
|
||
;You may obtain a copy of the License at
|
||
;
|
||
; http://www.apache.org/licenses/LICENSE-2.0
|
||
;
|
||
;Unless required by applicable law or agreed to in writing, software
|
||
;distributed under the License is distributed on an "AS IS" BASIS,
|
||
;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
;See the License for the specific language governing permissions and
|
||
;limitations under the License.
|
||
assume cs:code,ds:data,ss:stack
|
||
data segment;数据段
|
||
buf1 db 20 dup(0);用于接收用户输入的第一个加数
|
||
buf2 db 20 dup(0);用于接收用户输入的第二个加数
|
||
buf3 db 20 dup(0);用于存储最终需要输出的和的字符串形式
|
||
tmp db 20 dup(0);用于临时存储加法产生的结果,还需要对此字符串执行逆向存储
|
||
tip1 db 'Enter a value:$'
|
||
tip2 db 13,10,'Enter another value:$'
|
||
tip3 db 13,10,'Sum is:$'
|
||
data ends;数据段结束
|
||
stack segment stack;栈段
|
||
db 128 dup(?)
|
||
stack ends;栈段结束
|
||
code segment;代码段
|
||
|
||
;子过程:mul10
|
||
;作用:对一个0-10的数执行乘法,
|
||
;将其放大为10的cx次方,在本例中保证不会溢出
|
||
;入口:cx=这个数位在字符串中的位置,
|
||
; 比如说cx=1这个数就是字符串的个位
|
||
; ax=待放大的数
|
||
;出口:ax=放大过后的数
|
||
mul10 proc near
|
||
push cx;暂存cx的值
|
||
dec cx;很容易推得,实际要乘以10的次数是cx-1
|
||
jcxz ok;如果这个数在个位,那么就不用执行变换了
|
||
s0:push dx;循环开始:如果这个数不在个位,那么它需要被乘以10的(cx-1)次方
|
||
push bx;因为在循环中mul指令会破坏bx和dx,所以先暂存它们的值
|
||
mov bx,10;设置乘数为10
|
||
mul bx;mul指令隐式包含被乘数为ax,积为dx|ax,本例保证积不会溢出至dx
|
||
;因为输入不超过10000
|
||
pop bx;还原bx、dx的值
|
||
pop dx
|
||
loop s0;继续循环,直至指数降为0
|
||
ok:pop cx;恢复cx的值
|
||
ret;返回,此时ax为已被放大的数
|
||
mul10 endp
|
||
|
||
;子过程:ctod
|
||
;作用:输入一个字符串,并将其转化为二进制形式存储在寄存器中
|
||
;入口:ds:dx=缓冲区
|
||
;出口:ax=输入数的二进制形式
|
||
ctod proc near
|
||
push si
|
||
push cx
|
||
push bx;保护寄存器的值
|
||
mov si,dx;因为dx不能直接用来被寻址(只有bx、si和立即数)...
|
||
mov byte ptr ds:[si],5;所以通过si中转一下
|
||
;根据int 21h中断的0ah号功能的要求
|
||
;设置(ds:dx)为期望读取到的字符串的最大长度
|
||
push ax
|
||
mov ah,0ah
|
||
int 21h;执行系统调用,取得字符串
|
||
pop ax
|
||
inc si;si前移,此时si指向字符串的实际位数
|
||
sub cx,cx;cx清零
|
||
mov cl,[si];cx中存储有字符串的实际位数
|
||
inc si;si前移,此时si指向用户输入的内容
|
||
mov bx,0;将bx用作累加寄存器
|
||
s1:
|
||
mov ah,0;置ah为0
|
||
mov al,[si];将当前si所指的字符移入al
|
||
sub al,30h;al减30h,将ax转化为一个二进制数(当前si指向的数位的二进制形式)
|
||
call mul10;调用mul10将其转化为与当前数位对应的二进制数
|
||
;比如说当前si指向的是百位,且ax=2,执行mul10后ax=200
|
||
add bx,ax;将得到的值加到bi上
|
||
inc si;si后移,指向下一位
|
||
loop s1;loop指令隐含dec cx
|
||
mov ax,bx;将最终转化得到的二进制数移入ax
|
||
pop bx;恢复寄存器的值
|
||
pop cx
|
||
pop si
|
||
ret;返回,ax为读入的二进制数
|
||
ctod endp
|
||
|
||
;子过程:xchgstr
|
||
;作用:对si所指的字符串反向存储到di中
|
||
;入口:si=待反向存储的字符串
|
||
; di=缓冲区
|
||
; cx=字符串长度
|
||
;出口:di=已反向存储的字符串
|
||
xchgstr proc near
|
||
push si
|
||
push di
|
||
push cx
|
||
push ax;保护寄存器的值
|
||
s3:mov al,[si]
|
||
mov [di],al;执行数据交换
|
||
dec si;si前移
|
||
inc di;di后移
|
||
loop s3;循环至cx降为0
|
||
pop ax
|
||
pop cx
|
||
pop di
|
||
pop si;恢复寄存器的值
|
||
ret;返回,di为已反向存储好的字符串
|
||
xchgstr endp
|
||
|
||
;子过程:dtoc
|
||
;作用:将一个二进制数转化为以'$'结尾的字符串
|
||
;入口:dx=中转缓冲区
|
||
; bx=目标缓冲区
|
||
; ax=待转化的二进制数
|
||
;出口:bx=转化好的字符串
|
||
dtoc proc near
|
||
push bx
|
||
push dx
|
||
push ax
|
||
push di
|
||
push cx;保护寄存器的值
|
||
sub cx,cx;置cx为0
|
||
mov si,dx;设置si、di均指向中转缓冲区
|
||
mov di,dx
|
||
push bx;保护bx,bx存有目标缓冲区的地址
|
||
wk:mov dx,0;将ax除以10
|
||
mov bx,10
|
||
div bx;商在ax中,余数在dx中
|
||
add dx,30h;加30h使二进制数变成字符
|
||
mov [si],dl;将其移入中转缓冲区
|
||
inc si;si后移一位
|
||
inc cx;已处理的位数加一
|
||
cmp ax,0;如果商为0了,那么就处理完了...
|
||
jne wk;但不为0的话,还需要继续处理...
|
||
;循环结束的时候,si指向字符串末尾的后一个字符
|
||
;因为循环的逻辑是放一个字符然后si后移一位
|
||
;所以这里调整si让它指向字符串的末尾
|
||
;cx存储了字符串的位数,这个参数要传递给xchgstr
|
||
dec si
|
||
pop bx;取出目的缓冲区的地址
|
||
mov di,bx;赋给di
|
||
call xchgstr;将字符串反向存储
|
||
;注意xchgstr在返回之前已经调整了di使它指向目的缓冲区的首地址
|
||
add di,cx;让di指向字符串末尾的后一个字符
|
||
mov byte ptr [di],'$';赋予字符串结束标志,该标志被int 21h识别
|
||
pop cx
|
||
pop di
|
||
pop ax
|
||
pop dx
|
||
pop bx;恢复寄存器的值
|
||
ret;返回,此时bx指向的缓冲区存储有对应的字符串
|
||
dtoc endp
|
||
|
||
;程序的进入点
|
||
start:
|
||
mov ax,data
|
||
mov ds,ax
|
||
mov es,ax;设置ds和es寄存器,使他们同时指向数据段
|
||
;便于用si和di寻址
|
||
mov ax,stack
|
||
mov ss,ax
|
||
mov sp,127;设置ss寄存器和sp寄存器
|
||
lea dx,tip1
|
||
mov ah,09h
|
||
int 21h;向屏幕上输出第一条提示信息
|
||
lea dx,buf1;计算buf1的地址存入dx
|
||
call ctod;将其传入ctod,ctod返回的二进制数存储在ax中
|
||
mov bx,ax;将其移入bx
|
||
lea dx,tip2
|
||
mov ah,09h
|
||
int 21h;向屏幕上输出第二条提示信息
|
||
lea dx,buf2;计算buf2的地址存入dx
|
||
call ctod;将其传入ctod,ctod返回的二进制数存储在ax中
|
||
add ax,bx;两数相加
|
||
push ax
|
||
mov ah,09h
|
||
lea dx,tip3
|
||
int 21h;向屏幕上输出第三条提示信息
|
||
pop ax
|
||
lea dx,tmp;计算tmp的地址存入dx,tmp被用作中转缓冲区
|
||
lea bx,buf3;计算buf3的地址存入bx,buf3被用作目标缓冲区
|
||
call dtoc;调用dtoc以将ax中存储的二进制数转化为字符串
|
||
lea dx,buf3
|
||
mov ah,09h
|
||
int 21h;将转化后的字符串输出到屏幕上
|
||
mov ax,4c00h
|
||
int 21h;结束程序
|
||
code ends;代码段结束
|
||
end start;指明程序进入点为start |