正如本篇文章的标题所示:本篇文章主要是进行 [BX] 和loop的讲解
上篇文章我们讲述了 关于 自己去dosbox里面编写汇编程序并且一步一步的编译(masm) 链接(link)
然后进行debug的过程 ,也进行了一个关于栈的实验:
详情请见我的上一篇文章
Aseembly(八)-汇编语言编写程序
让我们正式进入内容
前言
-
怎么说呢.bx其实有点套娃的意思
我们看到 [] 这样的 ,一般认为是 做偏移 ,如:mov ax [0]
将地址为ds寄存器,偏移为[0]的值,传入ax
mov ax [1]
这里是偏移为1的值.
这里的偏移都是直接写出来的那么[bx]其实也是一样的,只不过给这个偏移取了一个变量.将所有的偏移放入到bx寄存器内.
如
mov ax.[bx]
这里就是将偏移量为bx的存入ax -
loop 其实就是循环
然后我们加入进一步的描述符,()
表示取括号内的地址的值
如:(2000H) 代表的就是2000H处的数值
(ax)表示ax中的内容
(al)表示al中的内容
((ds)16+(bx))表示内存单元为 ds16+bx的内容
()中的元素可以有三种类型:
1.寄存器 2.段寄存器名3 . 内存单元的物理地址 20位数据 如
(20000H) -
约定符号 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
- 在第一问的前提下,只是用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
下篇文章将介绍多个段的程序
敬请期待,点赞加关注,下一篇文章马上更新!