Aseembly(九)-[BX] Loop

正如本篇文章的标题所示:本篇文章主要是进行 [BX] 和loop的讲解

上篇文章我们讲述了 关于 自己去dosbox里面编写汇编程序并且一步一步的编译(masm) 链接(link)
然后进行debug的过程 ,也进行了一个关于栈的实验:
详情请见我的上一篇文章
Aseembly(八)-汇编语言编写程序

让我们正式进入内容

前言

  1. 怎么说呢.bx其实有点套娃的意思
    我们看到 [] 这样的 ,一般认为是 做偏移 ,如:

    mov ax [0]
    

    将地址为ds寄存器,偏移为[0]的值,传入ax
    mov ax [1]
    这里是偏移为1的值.
    这里的偏移都是直接写出来的

    那么[bx]其实也是一样的,只不过给这个偏移取了一个变量.将所有的偏移放入到bx寄存器内.

    mov ax.[bx]
    这里就是将偏移量为bx的存入ax

  2. loop 其实就是循环
    然后我们加入进一步的描述符,()
    表示取括号内的地址的值
    如:(2000H) 代表的就是2000H处的数值
    (ax)表示ax中的内容
    (al)表示al中的内容
    ((ds)16+(bx))表示内存单元为 ds16+bx的内容
    ()中的元素可以有三种类型:
    1.寄存器 2.段寄存器名3 . 内存单元的物理地址 20位数据 如
    (20000H)

  3. 约定符号 idata 这里指的就是 :

mov ax,[idata]

表示的就是 mov ax [1] mov ax [2] ....
再补充一条:inc
inc bx
bx中的内容自增1

1. [BX]

让我们来详细的看一下 [BX]
mov ax , [bx]
回顾下我们之前讲的内容:
这里实际上就是 将bx中的偏移地址EA 与段地址SA(DS)组合成:SA:EA 后取里面的内容:
也就是
(ax)=(SA)*16+(EA)
这里用王爽老师提出的问题来进行解答:
在这里插入图片描述
写出程序执行后的 20000H-21007H中的内容
来一步一步的分析下:
首先前两句

mov ax,2000H
mov ds,ax

就是为了给段地址寄存器赋值
随后的:
mov bx,1000H mov ax,[bx]是给bx中放入偏移地址
这里在执行完毕后,ax中的值为地址: 20000H+1000H=21000H处的内容
也就是BE
随后的
inc bx inc bx
bx连续自增两次,bx变为了 1002H
随后:
mov [bx] ,ax
这里将ax中的数据 存入到[bx]处
注意 这里存放的地方是: [bx]所指的内存处,并不是bx寄存器的值!!!
所以这里[bx]所指的地址也就是 21002H 处存放的为00beh
同样的道理: 21004H处也为00beH
后面的 21005H 21006H都为BE
如图:

在这里插入图片描述
很简单吧~

2. loop

loop的语法是这样的:
loop 标号随后cpu: (cx) = (cx) -1
直到cx为01为止.
来举个例子:
简单的计算2^2
我们不使用循环可以这样写:

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

这里使用的N+N代替了N*2

来写一下:2^3
也就是用N+N+N 代替N*3

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

如果计算的多了呢?如计算 2^12?
来这么写?

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

就很麻烦了
我们可以用循环来写:

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

这里可以看到在add ax,ax之前加入了s:
也就是用了标号s
随后loop跳到了s处,重复执行,每执行一次 cx的值-1
正好执行11次
来练练手~
计算123*236

assume cs:codesg
codesg segment 
	mov ax,0
	mov cx,236
s: add ax,123
loops 
	mov ax,4c00h
	int 21h
codesg ends
end

这里用123相加256次
我们还可以进一步优化:让256加123次:

assume cs:codesg
codesg segment 
	mov ax,0
	mov cx,123
s: add ax,256
loop s
	mov ax,4c00h
	int 21h
codesg ends
end

也是很简单的吧
让我们来思考以下问题:
将内寸 ffff:0006单元处的数*3 结果存到dx中
该怎么写呢?
可以考虑将数存在ax中,随后用dx进行累加
但是ax寄存器是16位的,这里的数是8位的哦
因此需要将ah 变为0
看代码吧:

assume cs:code 
code segment 
	 mov ax,0fffh
	 mov ds,ax
	 mov bx.06  ds:bx-> ffff6h

	mov al,[bx]

	mov ah,0
	
	mov dx,0
	mov cx,3

s: add dx,ax
loop s 
mov ax,4c00h
int 21h
code ends 
end 

其中: mov al,[bx]是将其中的低位赋值给al 随后高位用0来填充
保证了ax中的值与 我们期望的[bx]的值相等(在位数不一致的情况下)
我们在dosbox里面debug一下
在这里插入图片描述
这里完成了赋值过程
接下来开始循环
在这里插入图片描述
第一次执行后cx未变化 随后到了 loop指令 此时cx-1
在这里插入图片描述
随后dx开始继续变化
最后执行-p
在这里插入图片描述
很清晰了吧
汇编一点也不难
别说求三倍了,求123倍也可以
修改
mov cx,123
即可
只不过循环次数在debug时过长,使用p即可让cx为0 完成循环

3. Debug和 masm对指令的处理差异

对于同一条指令:
mov ax , [0]
在debug中 编程和在汇编源程序这种是不同的
在汇编源程序中的 会当成指令:
mov ax ,0
来进行
让我们来试验一下:
首先在debug中往内存处写如下指令:

mov ax,2000
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl.[2]
mov dl,[3]

在这里插入图片描述
再让我们用masm来看一下
在这里插入图片描述
可以看到,用masm进行编译的mov [] 系列的指令会变成 mov ax,0
上面我们所提到的idata就是这个意思:
在debug中
对于 mov al [idata]的没有影响,就是直接将其所在的内存地址中的数值存入到al中
但是在masm编译的汇编源程序中是会将idata本身存入到al中的
拿```mov al [1]``来讲
对于 debug来说,存入的是 ds:[1]
而对于masm来说,存入的是1
所以在我们进行汇编源程序编写的时候应该注意:
如果我们想将内容存入al这类寄存器,我们可以这样写:

assume cs:code
code segment 
	mov ax,2000h
	mov ds,ax
	mov bx,0
	mov al,[bx]
	
	mov ax,4c00h
	int 21h
code ends;
end

可以利用bx寄存器来将其写入到l内,但是这样会很麻烦,每次往l寄存器内存储都要这样写
所以提出了一种新的方法:

mov ax,2000h
mov ds,ax
mov al,ds:[0]

也就是在al类寄存器传入数值的时候写出段寄存器
所以在汇编源程序中,使用指令访问单元时,要加入段寄存器哦

4.组合一下 [bx]与loop

思考一个问题:
我们如何去计算内存地址:
ffff:0 -ffff:b单元中的数据和且放到dx中?

可以将其结果到dx中吗?可以的,因为每个内存单元为1字节,而dx为两字节
不会超出的

可以将数据直接累加到dx寄存器中吗? 不可以因为位数无法对其

那么可以直接将数据放到dl中,并且设dh=0吗?
也不可以 ,会导致溢出

因此我们有两种做法:
dx =dx +内存中的8位
dl = dl+ 内存中的8位

第一种做法运算对象的类型不匹配,第二种做法会溢出

那么我们只能:
拿一个16位的寄存器做中介,将内存中的8位数据放到16位里(ax)随后再将ax加到dx里就可以了

assume cs:code
code segment

mov ax,0ffffh
mov ds,ax

mov dx,0

mov cx,12
mov bx,0
s: mov al,ds:[bx]
    mov ah,0
    inc bx
loop s
mov ax,4c00h
int 21h

code ends

这里将bx接入循环,使其自增即可简便的完成

5.段前缀

```mov ax,[bx]`` 中默认了段是ds
其实等价于:

mov ax,ds:[bx]
除此之外还有:

mov ax,cs:[bx]
mov ax,ss:[bx]
mov ax,es:[bx]
mov ax,ss:[0]
mov ax,es:[0]

这里的ds cs ss es 都为段前缀 后面的为偏移

6 内存空间的安全问题

你们不觉得随意往内存空间写东西很危险吗


mov ax,1000h mov ds,ax mov al,0 mov ds:[0],al

假设内存空间:1000:0处存放着重要的系统代码

这里的
mov ds:[0],al
毫无疑问会将其改写 会变得十分危险
直接写结论:
写数据往 0:200-0:2ff内写没问题

7 练习:

将内存空间:ffff:0-ffff:b的内容复制到 0:200-0:20b中

assume cs:code 
code segment 
mov bx,0
mov cx,12
s: mov ax,0ffffh
	mov ds,ax
	mov dl,ds:[bx]

	mov ax,0020h
	mov ds,ax
	mov ds:[bx],dl

	inc bx 
loop s
mov ax,4c00h
int 21h
code ends

这样公用一个段寄存器效率太低了,因此改进下用两个:
代码如下:

assume cs:code 
code segment
	mov dx,0
	mov cx,12
	mov bx,0
	mov ax,0ffffh
	mov ds,ax
	mov ax,0020h
	mov es,ax
s: mov dl,[bx]
	mov es:[bx],dl
	inc bx
loop s 
	mov ax,4c00h
	int 21h
code ends
end

很简单吧~
继续来个练习
1.向内存0:200-0:23F传送数据 0-63(3FH)

 assume cs:code 
code segment 
    mov ax, 0200h      
    mov ds, ax
    
    mov bx, 0          
    mov dx, 0         
    mov cx, 03fh      

s:  mov [bx], dx      
    inc dx            
    inc bx            
    loop s           

    mov ax, 4c00h    
    int 21h

code ends
end
  1. 在第一问的前提下,只是用9条指令完成功,不包括开始的assume和 code segement
    但是包括mov ax,4c00h,int 21h
assume cs:code 
code segment 
mov ax,0200h
mov ds,ax
xor di,di 
mov cx,0040h
s: stosb
inc al 
loop s 
mov ax,4c00h
int 21h
code ends 
end

3.补全代码:

assume cs:code 
code segment 
mov ax, ?
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,?
s: mov al,[bx]
	mov es:[bx],al
	inc bx
	loop s
	mov ax,4c00h
	int 21h
code ends
end

答案:

assume cs:code
code segment 
	mov ax,cs
	mov ds,ax
	mov ax,0020h
	mov es,ax
	mov bx,0
	mov cx,17
s:mov al,[bx]
	mov es:[bx],al
	inc bx
loops 
mov ax,4c00h
int 21h
code ends
end

下篇文章将介绍多个段的程序
敬请期待,点赞加关注,下一篇文章马上更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值