汇编语言笔记5[bx]和loop指令

汇编语言笔记5[bx]和loop指令

0x00 [bx]

​ 在前面有[0]表示偏移地址为0,[bx]即表示偏移地址为bx中的内容

​ mov ax,[0]表示将一个内存字单元送入ax,这个内存字单元的偏移地址是0 段地址在ds中

​ mov al,[0]则表示将一个内存单元送入al,偏移地址是0,段地址在ds中

​ 要描述一个内存单元,需要 1 内存单元的地址 2 内存单元的长度

​ [bx]同样可以表示一个内存单元,他的偏移地址在bx中

​ mov ax,[bx] mov al,[bx]

0x01 loop

​ loop即使高级语言中的循环指令,其循环次数由cx中的内容决定

​ loop指令的格式是 loop 标号

​ cpu执行loop指令时,要进行两步操作 1 cx=cx-1 2判断cx中的值是否为0,不为零则跳转至标号处执行,为零则向下执行

assume cs:codesg
codesg segment
		mov ax,1
		mov cx,12
s:		add ax,ax
		loop s
		
		mov ax,4c00h
		int 21
codesg ends
end

​ 上述代码计算2^12 初始状态ax累加器为1 =2^0 cx为1时,add ax,ax 会被执行1次ax=2 =2^1 所以cx与指数相同

​ 上面的s即是一个标号,编译器将其编译成一个地址,当cx不为0时,会跳到这个地址继续执行

QQ截图20200529093719

​ assume cs:codesg

​ codesg segment

​ mov ax,0

​ mov cx,123

​ s: add ax,236

​ loop s

​ codesg ends

​ end

​ 123*236 可以解释为123个236相加 而不是236个123相加,因为前者运算次数少,16位最大能表示256 x

256 因此ax作累加器不会越界

QQ截图20200529094359

​ 是将一个内存单元中的内容*3,由于内存单元为8为最大为256 dx16位不会越界,所以是可行的

​ assume cs:codesg

​ codesg segment

​ mov ax,0ffffh

​ mov ds,ax

​ mov bx,6

​ mov al,[bx]

​ mov ah,0

​ mov dx,0

​ mov cx,3

​ s: add dx,ax

​ loop s

​ mov 4c00h

​ int 21

​ codesg ends

​ end

​ 注意ffffh 要写成 0ffffh 因为数据不能以字母开头,所以前面要加0

​ 在执行到loop命令想要跳过时,可以用p命令跳过loop 可以用g 地址 的方式直接执行到某一条指令处,避免一次一次的按t

0x02 debug和masm对指令的不同处理

QQ截图20200529100021

QQ截图20200529100037

​ 1在masm编译中,如果表达内存单元偏移地址时idata(常数),要么用mov ax,ds:[idata]显式给出段寄存器的方式,要么 先将idata 赋值给bx,才可以隐式的使用段寄存器ds 比如 mov bx,0 mov ax,[bx]这里的段寄存器默认为ds,也可以手动指出

​ 2在debug中,所有数据都默认是16进制 不论加不加h 而masm中必须结尾加上h才识别为16进制 默认是10进制

0x03 loop和[bx]的联合应用

QQ截图20200530084431

​ 对于这个问题,要十分小心,1,dx作为累加器,结果会不会超出dx能表达的范围? dx为16位寄存器 最大能储存的数是 256*256 而0-b的内存单元是12x256,因此不会越界 2,能直接依次累加吗? dx为16位寄存器,直接add dx,ds:[bx] 会将一个字型的数据与dx累加而不是一个字节,因此我们得找个8位寄存器来获取每一个内存单元的值,直接用dl作累加器行吗?不行,结果可能越界了。那该怎么办? 将每个内存单元 赋值给 al ,然后ah置0 这样 ax的值就和一个内存单元对应,然后再累加到dx中,重复这个过程,代码如下

assume cs:codesg
codesg segment
		mov ax,0ffffh
		mov ds,ax
		mov bx,0
		mov cx,0ch
s:		mov al,ds:[bx]
		mov ah,0
		add dx,ax
		inc bx
		loop s
		
		mov 4c00h
		int 21h
codesg ends
end

​ 如果要处理地址连续的内存单元中的数据访问问题,可以用循环,通过改变偏移地址[bx]来解决

0x04 段前缀

​ 指令 mov ax,[bx] 内存单元的偏移地址由bx给出,而段地址默认由ds给出,我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器 比如:

​ mov ax,ds:[bx] mov ax,cs:[bx] mov ax,ss[bx]等等 这些称为段前缀

QQ截图20200530091357

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

​ 有了段前缀,对于不同的段我们可以用不同的段寄存器保存,而不用来回修改ds,简便很多

0x05 内存读写的安全性

assume cs:codesg
codesg segment
		mov ax,0
		mov ds,ax
		mov ds:[26h],ax
		
		mov ax,4c00h
		int 21h
codesg ends
end

​ 上述代码编译连接均没有问题,然而执行到mov ds:[26h],ax时 debug直接卡死了,原因在于非法的内存访问

​ 我们是在虚拟8086模式,该模式是虚拟的保护模式而不是实模式,由dos系统管理所有资源,也包括内存,所以如果我们需要向内存空间写入数据,应该要使用操作系统给我们分配的内存空间,而不是直接用地址任意指定内存单元,下一章 将会学习如何使用操作系统给我们分配的空间

QQ截图20200530091126