Assembly-8086-Learn/apb.asm

192 lines
6.5 KiB
NASM
Raw Permalink Normal View History

2022-08-24 22:56:50 +08:00
;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;保护bxbx存有目标缓冲区的地址
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;将其传入ctodctod返回的二进制数存储在ax中
mov bx,ax;将其移入bx
lea dx,tip2
mov ah,09h
int 21h;向屏幕上输出第二条提示信息
lea dx,buf2;计算buf2的地址存入dx
call ctod;将其传入ctodctod返回的二进制数存储在ax中
add ax,bx;两数相加
push ax
mov ah,09h
lea dx,tip3
int 21h;向屏幕上输出第三条提示信息
pop ax
lea dx,tmp;计算tmp的地址存入dxtmp被用作中转缓冲区
lea bx,buf3;计算buf3的地址存入bxbuf3被用作目标缓冲区
call dtoc;调用dtoc以将ax中存储的二进制数转化为字符串
lea dx,buf3
mov ah,09h
int 21h;将转化后的字符串输出到屏幕上
mov ax,4c00h
int 21h;结束程序
code ends;代码段结束
end start;指明程序进入点为start