BX+CX+loop

首先定义一下符号:

描述性符号: “()”

为了描述上的简洁,我们使用一个描述性的符号"()"来表示一个寄存器或一个内存单元中的内容,比如:

  • (ax)表示ax中的内容、(al)表示al中的内容。
  • (20000H)表示内存20000H单元的内容。
  • ((ds)*16+(bx))表示ds中的内容为ADR1,bx中的内容为ADR2,内存ADR1*16+ADR2单元的内容。这同样可以理解为,ds中的ADR1作为段地址,bx中的ADR2作为偏移地址,内存ADR1:ADR2单元的内容。

注意"()"中的元素可以有三种类型:寄存器名、段寄存器名、内存单元的物理地址(一个20位数据)。比如:

(ax)、(ds)、(al)、(cx)、(20000H)、((ds)*16+(bx))等是正确的用法。

我们来看下(X)的应用:

  • ax中的内容为0010H,可以这样来描述:(ax)=0010H
  • 2000:1000处的内容为0010H,可以这样来描述:(21000H)=0010H
  • 对于mov ax,[2]的功能,可以这样来描述:(ax)=((ds)*16+2)
  • 对于mov [2],ax的功能,可以这样来描述:((ds)*16+2)=(ax)
  • 对于add ax,2的功能,可以这样来描述:(ax)=(ax)+2
  • 对于add ax,bx的功能,可以这样来描述:(ax)=(ax)+(bx)
  • 对于push ax的功能,可以这样来描述:
    (sp)=(sp)-2
    ((ss)*16+(sp))=(ax)
  • 对于pop ax的功能,可以这样来描述:
    (ax)=((ss)*16+(sp))
    (sp)=(sp)+2

注意(X)所表示的数据有两种类型,字节和字,是哪种类型由寄存器名或具体的运算决定,比如:

  • (al)、(bl)、(cl)等得到的数据为字节型;(ds)、(ax)、(bx)等得到的数据为字型
  • (al)=(20000H)得到的数据为字节型;(ax)=(20000H)得到的数据为字型

符号idata表示常量

比如:

  • mov ax,[idata]就代表mov ax,[1]、mov ax,[2]、mov ax,[3]等
  • mov bx,idata就代表mov bx,1、mov bx,2、mov bx,3等

一、[BX]

[bx]和[0]有些类似,[0]表示段地址在ds中,偏移量为0的内存单元,[bx]同样也表示一个内存单元,它的偏移地址在bx中,比如下面的指令:

mov ax,[bx]

将一个内存单元中的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ds中。即:(ax)=((ds)*16=(bx))

mov al,[bx]

将一个内存单元的内容送入al,这个内存单元的长度为1字节(字节单元),存放一个字节,偏移地址在bx中,段地址在ds中

二、Loop指令

loop指令的格式是:loop 标号,CPU执行loop指令的时候,要进行两步操作:

  1. (cx)=(cx)-1
  2. 判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。

从上面的描述中,可以看到,cx中的值影响着loop指令的执行结果。通常我们用lool指令来实现循环功能,cx中存放循环次数。

接下来通过一个程序来看下loop指令的具体应用:

任务1:编程计算 2 2 2^2 22,结果存在ax中。

( a x ) = 2 (ax)=2 (ax)=2,可计算 ( a x ) = ( a x ) × 2 (ax)=(ax)\times2 (ax)=(ax)×2,最后 ( a x ) (ax) (ax)中为 2 2 2^2 22的值。 N × 2 N\times2 N×2可用 N + N N+N N+N实现,程序如下:

assume cs:code
code segment
	mov ax,2
	add ax,ax
	mov ax,4c00h
	int 21h
code ends
end

但是按照上面的算法,计算 2 12 2^{12} 212需要11条重复的指令 add ax,ax。这种情况下,我们就可以用loop来简化我们的程序。

程序5.1

assume cs:code
code segment
	mov ax,2
	mov cx,11
s:	add ax,ax
	loop s
	mov ax,4c00h
	int 21h
code ends
end

接下来分析下程序5.1

(1)标号
在汇编语言中,标号代表一个地址,程序5.1中有一个标号s。它实际上标识了一个地址,这个地址处有一条指令:add ax,ax
(2)loop s
CPU执行loop s的时候,要进行两步操作

  1. (cx)=(cx)-1
  2. 判断cx中的值,不为0则转至标号s所标识的地址处执行(这里的指令是add ax,ax),如果为零则执行下一条指令(下一条指令是mov ax,4c00h)

(3)以下三条指令

	mov cx,11
s:	add ax,ax
	loop s

执行loop s时,首先要将(cx)减一,然后若(cx)不为0,则向前转至s处执行add ax,ax。所以,可以利用cx来控制add ax,ax的执行次数。

用cs和loop指令相配合实现循环功能的3个要点:

  1. 在cx中存放循环次数
  2. loop指令中的标号所标识地址要在前面
  3. 要循环执行的程序段,要写在标号和loop指令的中间。

用cx和loop指令相配合实现循环功能的程序框架如下:

	mov cx,循环次数
s: 
	循环执行的程序段
	loop s

五、loop和[bx]的联合应用

考虑这样一个问题,计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。

分析下可能出现的问题:

  • 运算后的结果是否会超出dx所能存储的范围?
    ffff:0~ffff:b内存单元中的数据是字节型数据,范围在0~255之间,12个这样的数据相加,结果不会大于65535,可以在dx中存放下
  • 能否将ffff:0~fff:b中的数据直接累加到dx中?
    不可以,因为ffff:0~ffff:b中的数据都是8位的,不能直接加到16位寄存器dx中
  • 能否将ffff:0~ffff:b中的数据累加到dl中,并设置(dh)=0,从而实现累加到dx中?
    这也不行,因为dl是8位寄存器,能容纳的数据的范围在0~255之间,ffff:0~ffff:b中的数据也都是8位,如果仅向dl中累加12个8位数据,有可能造成进位丢失

从上面的分析中,可以看出两个主要问题:类型的匹配和结果的不越界。解决的方法就是用一个16位寄存器来做中介,将内存单元中的8位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使得两个运算对象的类型匹配并且结果不会越界。

程序如下:

程序5.5

assume cs:code

code segment
	mov ax,ffffh
	mov ds,ax				//设置(ds)=ffffh
	
	mov dx,0					//初始化累加寄存器,(dx)=0

	mov al,ds:[0]
	mov ah,0					//(ax)=((ds)*16+0)=(ffff0h)
	add dx,ax				//向dx中加上ffff:0单元的数值
	
	mov al,ds:[1]
	mov ah,0					//(ax)=((ds)*16+1)=(ffff1h)
	add dx,ax				//向dx中加上ffff:1单元的数值

	mov al,ds:[2]
	mov ah,0					//(ax)=((ds)*16+2)=(ffff2h)
	add dx,ax				//向dx中加上ffff:2单元的数值
	
	..........

	mov al,ds:[0bh]
	mov ah,0					//(ax)=((ds)*16+0bh)=(ffff0bh)
	add dx,ax				//向dx中加上ffff:0bh单元的数值

	mov ax,4c00h			//程序返回
	int 21h

code ends
end

上面的程序很简单,但是存在大量重复。所以可以使用loop来改进上面的程序:

程序5.6

assume cs:code
code segment
	mov ax,0ffffh
	mov ds,ax
	mov bx,0				// 初始化ds:bx指向ffff:0

	mov dx,0				//初始化累加寄存器dx,(dx)=0

	mov cx,12			//初始化循环计数寄存器cx,(cx)=12

s:	mov al,[bx]
	mov ah,0
	add dx,ax			//间接向dx中加上((ds)*16+(bx))单元的数值
	inc bx					//ds:bx指向下一个单元
	loop s

	mov ax,4c00h
	int 21h

code ends
end

六、段前缀

我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。比如:

  • mov ax,ds:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ds中。
  • mov ax,cs:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在cs中
  • mov ax,ss:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ss中
  • mov ax,es:[bx]
    将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在es中

这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的"ds:", “cs:”, “ss:”, “es:”,在汇编语言中称为段前缀。

七、段前缀的使用

考虑一个问题,将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。

程序如下:

程序5.8

assume cs:code
code segment
	mov bx,0					//(bx)=0,偏移地址从0开始
	mov cx,12				//(cx)=12,循环12次

s:	mov ax,0ffffh
	mov ds,ax				//(ds)=0ffffh
	mov dl,[bx]				//(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl

	mov ax,0020h
	mov ds,ax				//(ds)=0020h
	mov [bx],dl				//((ds)*16+(bx))=(dl),将dl的数据送入0020:bx

	inc bx						//(bx)=(bx)+1 
	loop s

	mov ax,4c00h
	int 21h

code ends
end

因源始单元ffff:X和目标单元0020:X相距大于64KB,在不同的64KB段里,程序5.8中,每次循环要设置两次ds。这样做是正确的,但是效率不高。我们可以使用两个段寄存器分别存放源始单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。

改进的程序如下:

程序5.9:

assume cs:code
code segment
	mov ax,0ffffh
	mov ds,ax

	mov ax,0020h
	mov es,ax

	mov bx,0

	mov cx,12

s:	mov dl,[bx]
 	mov es:[bx],dl
 	inc bx
 	loop s

	mov ax,4c00h
	int 21h

code ends
end

程序5.9中,使用es存放目标空间0020:0~0020:b的段地址,用ds存放源始空间ffff:0~ffff:b的段地址。在访问内存单元的指令"mov es:[bx],al"中,显式地用段前缀"es:"给出单元的段地址,这样就不必在循环中重复设置ds。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给出以下汇编语言代码的完整的注释data segment shuc db 'draw a yuan: $' hua1 db 'input yuanxin and banjing(example:310,220 200): $' zifu db 20 dup(0) ;此段用以临时存放输入字符 shu db 20 dup(0) ;再次存放输入数字的ASCII码转换华为的数字 suan db 24 dup(0) ;用来存放计算圆过程中产生的临时数据 data ends stack segment stk db 16 dup(0) stack ends code segment assume cs:code, ds:data,ss:stack start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov dx,offset shuc call showmsg call input mov al,ds:[si] and al,11011111b cmp al,43h draw1: mov dx,offset hua1 call showmsg call input call zhuanshu call moshi mov bx,offset shu mov ax,ds:[bx] mov si,ax mov ax,ds:[bx+2] mov di,ax mov ax,ds:[bx+4] call drawyuan mov ax,4c00h int 21h input: mov bx,0 mov cx,20 re: mov ah,1h int 21h cmp al,0dh jz scx mov si,offset zifu mov [bx][si],al inc bx loop re ret scx: mov cx,0 ret showmsg: mov ah,9h int 21h ret moshi: mov al,12h mov ah,0 int 10h ret zhuanshu: mov bx,offset zifu mov bp,offset shu mov cx,16 mov si,0 mov di,0 lei: mov al,ds:[bx][si] cmp al,0 jz scx sub al,30h mov dl,100 mul dl mov word ptr ds:[bp][di],ax mov ax,0 mov al,ds:[bx][si+1] sub al,30h mov dl,10 mul dl add ax,word ptr ds:[bp][di] mov word ptr ds:[bp][di],ax mov ax,0 mov al,ds:[bx][si+2] sub al,30h add ax,word ptr ds:[bp][di] mov word ptr ds:[bp][di],ax add si,4 add di,2 loop lei ret drawyuan: mov bx,offset suan mov word ptr ds:[bx],si mov word ptr ds:[bx+2],di sub si,ax mov word ptr ds:[bx+4],si sub di,ax mov word ptr ds:[bx+6],di shl ax,1 mov cx,ax mov word ptr ds:[bx+10],ax shr ax,1 xor dx,dx mul ax mov word ptr ds:[bx+12],ax mov word ptr ds:[bx+14],dx hang: push cx mov cx,ds:[bx+10] mov dx,di mov si,ds:[bx+4] lie: push cx push dx xor dx,dx mov cx,si mov ax,si sub ax,ds:[bx] xor dx,dx imul ax mov word ptr ds:[bx+16],ax mov word ptr ds:[bx+18],dx mov ax,di sub ax,ds:[bx+2] xor dx,dx imul ax add ax,ds:[bx+16] adc dx,ds:[bx+18] mov word ptr ds:[bx+20],ax mov word ptr ds:[bx+22],dx pop dx mov ax,ds:[bx+22] cmp ax,ds:[bx+14] jne kong mov ax,ds:[bx+12] sub ax,ds:[bx+20] cmp ax,500 ja kong mov ah,0ch mov al,5h int 10h kong: pop cx inc si loop lie pop cx inc di loop hang ret code ends end start
最新发布
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值