第5章 [bx]和loop指令
1,[bx]是什么呢
与[address]类似,比如[0]表示DS段上偏移地址为0的内存单元
那这不就是把偏移地址放在寄存器里面了吗?没看出有什么区别
继续往下看
2,loop是什么
loop简单的说就像是python中的for循环,至于怎么用,下面会讲到
3,定义描述性性符号()
4,约定符号idata表示常量
5.1[bx]
还真的就是把偏移地址放到一个寄存器里面,感觉有点多此一举
下面是一个使用[bx]的例子
5.2 loop指令
从对loop指令的格式和操作中不难看出,cx中存储的是loop的循环次数
至于标号是什么,可以通过一个具体的实例了解
这里不用去看伪指令,这段程序的功能实现了计算2^12,标号就是左边的S
当程序从上到下被CS:IP指向并执行时,在loop s执行时,CX寄存器中的值会-1
也就是 (cx)-1
同时又会继续跳到标号S指向的指令执行
当cx中的值为0时,就不会循环,而是继续向下执行指令
5. 3 在debug中跟踪loop
这里提到了如何将一个字节的数据赋值到一个16位寄存器的问题
如果要将一个字节数据1AH存入AX寄存器
那么AX寄存器中的值应为001AH,而我们知道,在将一个内存单元的内容给寄存器时,会顺带将它的下一个内存单元一起发送给寄存器,这样(AX)=XX1AH,达不到我们的目的
所以我们可以用AL取出1AH,再将AH赋值为00
mov ah,00
mov al,[1] 假设1AH在DS段的偏移量为1
这样就实现了将一个字节数据的赋值
5.4 Debug和汇编编译器Masm对指令的不同处理
通过这章bx似乎有点用了,但感觉还是没什么用
5.5 loop和[bx]的联合应用
后面代码的具体实现就是将ah变为00H,只改变al的值(取出每个对应内存单元的数据)
然后将整个ax的值加到bx中
mov ah,0
mov al,ds:[0]
add bx,ax
mov al,ds:[1]
add bx,ax
下面就省略了
如果一直写下去,会发现有很多重复的代码,它们只是值改变了,但对应的功能没变
面对这种问题,在编程中我们都会想到循环这个功能
那汇编语言里有没有这样简单高效的方法呢?
有的兄弟,有的
我们之前觉得没什么用的[bx]也是站起来了
在循环中继续使用[idata]这种方式就不适用了,它只能访问固定的内存单元
而[bx]就可以动态的访问内存单元了,这就很方便了
5.6段前缀
5.7一段安全的空间
后面举例了随意的向内存空间中写入指令而造成的死机状态
因为在操作系统的环境中工作,操作系统管理所有资源,所以我们应该尽可能用操作系统分配的内存空间来操作,而不是随意的使用内存空间
5.8段前缀的使用
总的来说就是将一段内存地址的数据给到另一端内存地址的数据时,我们可以用之前的loop和[bx]的方法来实现,但是[bx]默认的段地址是ds,所以我们每次都要重新设置ds,即第一次在要搬运的内存地址,第二次是目标内存地址
这样做的可以实现但效率很慢
所以我们可以用一个空的段地址寄存器来实现程序的改进,用段地址:[bx]的方式来访问对应的段
比如
第6章 包含多个段的程序
6.1 在代码段中使用数据
用debug查看程序,会发现程序开始的不是我们编写的汇编代码 这是因为我们存入的字型数据和指令都是二进制数据,当CS:IP指向它时,也会被执行
这也说明了数据和指令本质上没有区别,只有CS:IP指针指向的数据才是指令
但是我们就这样直接运行的话会出现我们不想要的结果,就像这样将我们的数据当成指令进行,执行了我们不需要的命令,可能会对程序本身造成影响
想要正常执行命令就要设置IP=10H,让IP指针跳过前16个字型数据
那有没有解决办法呢?
有的,加入标号start
这里的start也是伪指令,并不会被当做汇编代码执行
6.2代码段中使用栈
由于栈上的数据是遵循LIFO的,即后进先出
所以应该是将数据从前往后入栈,再一次出栈就可以达到逆序存放的效果了
因为栈的地址是从高到低增长的,所以空栈时要指向栈顶,即地址最大处 。再push入栈时SS:SP会随着每次出栈,SP-2,最后到SP=16,也就是所谓的栈底。
而mov sp:32是因为定义了16个字型数据,也就是32个字节,换成十六进制表示就是
mov sp:20H
6.3 将数据、代码、栈放入不同的段
想要实现这个功能很简单,像定义代码段一样多定义一段就行了
我们只看关键内容,即如何定义多个段
可以看出定义一个段也是段名+segement
只要在最后将入口定义在start段我们就不用担心其他指针的操作了
刚刚去搜了一下,dw是定义字符的意思,每个数据占两个字节,db是定义字节的意思,每个数据占一个字节
注意了,这里的data代表的段地址,是一串数据,不能直接给到ds这个段寄存器中
而且我们定义的段cpu是无法理解的,我们只是人为地在内存中划分出不同的空间来存放我们的代码和数据
第7章 更灵活的定位内存地址的方法
7.1 and和or指令
从图中可以看出与运算与and的逻辑运算类似
每位数据对应运算,两个都为1则为1,只要有一个不是1或者全是0则为0
只要有一个是1,则或运算后就是1,与or的逻辑运算相同
7.2关于ASCII码
简单的说就是我们能识别的字,语言等,计算机是看不懂的。计算机只看得懂01,所以我们要人为定义一串01数,来让它表示我们能识别的语言。
ASCII码 - 基本ASCII码和扩展ASCII码,中文最全ASCII码对照表0~255
7.3 以字符形式给出的数据
7.4 大小写转换的问题
改变一个字符的大小写就是改变它对应的ASCII字符,英文字符的大小写每个相差20H
所以要全部转大写或者转小写,需要判断它是在大写的范围里还是小写的范围里
这里要用到条件判断,那汇编语言中要怎么用条件判断呢?
书中给出的大答复是……
所以我们要该大写只要将对应的二进制数据的第5位改为0就行了
因为大写字母本来就是0,而小写是1,所以我们可以用and运算,1 and 0变成0
改小写同理,用or, 1 or 0变成1
不过它这里为什么是从右往左数的啊
7.5 [bx+idata]
从这里也可也看出来为什么高级语言中往往不能用纯数字来命名一个函数
我猜测应该是每个函数都对应一个段,要是将函数命名为纯数字,在访问这个段的内容时就会像上面一样,不能判断是段地址+位移地址,还是[idata+bx]
7.6 用[bx+idata]的方式进行数组的处理
这里就是与之前的大小写转化问题关联了起来,我们知道了大小写转换的原理,问题在于访问对应数据。按之前的思路我们就只能一个一个字符串转换,这样要用到两个循环
而用了[idata+bx]之后用一个循环就可以搞定了
但这样看也仅使用与相同长度的字符串,还是判断更好用一点
而且可以看到汇编语言中的大小写转换在c语言中同样适用
7.7 SI和DI
这样我们就多了几个偏移地址寄存器,可以同时进行多个数据的访问
不过从后面的实例代码看,这两个指针也是默认段地址为ds
7.8 [bx+si]和bx+di]
可能这样我们还不能体会到“灵活”这个feel
我们用一个实例代码体会一下
由两个寄存器,相当于两个变量结合,这样灵活度会大大提升
7.9[bx+si+idata]和[bx+di+idata]
搁这儿套娃呢
从其他格式可以看到,bx,si,idata的顺序是没有影响的
7.10不同的寻址方式的灵活应用
后面还遇到了一个关于计数器的问题
比如这里s0和s公用一个计数器cx,而当s循环完后,(cx)=0
这样到loop s0就不会循环了,那解决办法是什么呢
解决办法就是将cx的值先保存到一个通用寄存器中
妙啊,妙啊
这里开辟一段内存空间,我觉得也可以用栈来存储
作者是在数据段中多定义了一个字,用来充当寄存器的功能
访问时用 ds[64]就可以了(前面定义了4个16字节数据)
果然还得用栈,这样体现了栈的意义,用来暂存数据