第5章 [BX]和loop指令

  • 描述一个内存单元需要两种信息:内存单元的地址和内存单元的长度。用[…]表示一个内存单元时,…表示单元的偏移地址,段地址默认在DS中,单元的长度可以由具体指令中的其他操作对象(比如寄存器)指出。
  • 本书定义了一个描述性符号:“()”,用此符号表示一个内存或者寄存器中的内容,跟汇编语言没关系,只是为了描述的简洁。"()"中的元素可以有3种类型:寄存器名、段寄存器名、内存单元的物理地址。
  • 约定符号idata表示常量,这也是为了本书的描述简洁,跟汇编语言没关系。mov ds,idata这是一条非法指令。因为idata代表常量,而 8086CPU不支持直接将数据送入段寄存器的操作

5.1 [BX]


    这一小节很简单,就是将内存单元的偏移地址放到了寄存器中[bx];还有一个自增1的指令inc,正好最近背单词学过单词increase,inc就是简写的increase。难怪说外国人(英语区)学编程方便。


5.2 Loop指令

    loop指令的格式:Loop 标号;在汇编语言中,标号代表一个地址。CPU执行loop指令的时候,要进行两步操作,1.(cx)=(cx)-1;2.判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。

CX和Loop指令配合实现循环功能的3个要点:
在cx中存放循环次数;
loop指令中的标号所标识地址要在前面;
要循环执行的程序段,要写在标号和loop指令的中间;



5.3 在Debug中跟踪用loop指令实现循环

    这小节问题分析中第3个问题要注意一下“ffff:6单元是一个字节单元,ax是一个16位寄存器,数据长度不一样,如何赋值?”
    刚开始我觉得很简单就是 mov ax,[6],看了书中代码要拆分成两部分mov ah,0;mov al,[6],我想起了书中前面的三个知识点,“在进行数据传送或运算时,要注意指令的两个操作数的位数应该一致”;“描述一个内存单元需要两种信息:内存单元的地址和内存单元的长度”;“单元的长度可以由具体指令中的其他操作对象(比如寄存器)指出”因为ax是16位,mov ax,[6]会将内存中两个连续的字节内容传送给ax寄存器,这不符合问题是将内存中一个字节的内容乘3送入寄存器ax

    大于9FFFH的十六进制数据A000H、A001H…等,在书写时都是以字母开头。而 汇编源程序中,数据不能以字母开头,所以要在前面加0。比如,A000H在汇编源程序中要写为“0A000H” 。

两个debug命令的用法
-g "g 0012"将使debug从当前的CS:IP指向的指令执行,一直到(IP)=0012H为止。
-p 我们希望将循环一次执行完。可以使用p命令来达到目的。再次遇到loop指令时,使用p命令来执行,Debug就会自动重复执行循环中的指令,知道(cx)=0为止。

    如果你也是自己根据书上的问题,自己先思考问题然后写代码解决,再跟书上的代码对照,那么你可能碰到跟我一样的问题。就是源码中形如[…]这样的对内存访问,在masm编译器和debug中的解释不同,刚发现这个问题时我以为我哪儿写错了,自己的代码反复改就是跟预想的不一样,着急上火了很长时间,最后快放弃了,翻了一页纸,找到了原因,不是我的问题,是masm和debug的问题,就是下一节的内容。侧面反映了这本书虽然教的入门知识,但是编书的人很严谨。


5.4 Debug和汇编编译器masm对指令的不同处理

Debug和编译器对源程序指令中的“[idata]”有不同的解释
Debug将它解释为“[idata]”是一个内存单元,“idata”是内存单元的偏移地址
编译器将“[idata]”解释为“idata”,就是一个常量
如何实现通过[…]形式访问内存单元的数据呢?有两个方法,个人倾向第二个。
将偏移地址送入寄存器,比如bx中,用[bx]的方式来访问内存单元
还是希望能在“[…]”中直接给出内存偏移地址,就要在“[…]”的前面显示的给出段地址所在的段寄存器。不能用段地址默认在DS中的形式了

    在写13.2节的练习时,我发现我忽略了这小节的知识点 “在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用‘[…]’来表示内存单元,如果‘[…]’是一个常量直接给出内存单元的偏移地址,就要在[…]的前面显示的给出段地址所在的段寄存器。比如‘mov al,ds:[0]’,如果‘[…]’里不是常量,是寄存器,比如‘mov al,[bx]’就不会报错,段寄存器根据bx bp si di 选择不同的默认段寄存器”


5.5 loop和[bx]的联合应用



5.6 段前缀

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


5.7 一段安全的空间

    不能确定一段内存空间中是否存放着重要的数据或代码的时候,不能随意向其中写入内容。我们在操作系统中工作,操作系统管理所以的资源,包括内存在内。如果需要向内存中写入数据的话,要使用操作系统给我们分配的空间,而不应直接用地址任意指定内存单元,向里面写入。

实模式和保护模式下,直接去操作内存的区别
在纯DOS方式实模式下(不是那种虚拟8086模式,那还是保护模式下对实模式的兼容),可以不理会DOS,直接用汇编语言去操作真实的硬件, 因为运行在CPU实模式下的DOS,没有能力对硬件系统进行全面、严格的管理
在windows、unix这些运行在CPU保护模式下的操作系统中,不理会操作系统,用汇编语言去操作真实的硬件,是根本不可能的。 硬件已经被操作系统利用CPU保护模式所提供的功能全面严格的管理了

    在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用0:200 ~ 0:2ff(00200h ~ 002ffh)的256个字节空间。所以我们使用这段空间是安全的。为了谨慎起见,进入DOS后,用debug查看一下这段空间的内容是否全为0 。




5.8 段前缀的使用


    访问内存单元的指令中,要灵活的运用段前缀,不要因为使用ds、cs、ss比较多,就忽略了段前缀es:。
    汇编中段的概念是“一段起始地址是16的整数倍的连续的64KB的空间”,只要符合段概念的要求就是段。我们可以更改段地址以方便我们解决问题。比如这小节的例题中,0:200~0:20b单元等同于0020:0 ~ 0020:b单元,它们描述的是同一段空间,只是段地址不同,但是改成后面的形式就方便了我们解决问题。我是因为之前先看了保护模式相关的资料,对gdt、ldt、idt还有段描述符有一定了解,内心中总是存有在保护模式下的段,不能更改的印象,所以需要反复提醒自己现在学的是汇编,是在实模式下,有时要灵活的运用段地址。


实验4 [bx]和loop的使用


    实验中1、2两个问题很简单,注意一下loop指令的循环次数,我一开始忽略 loop指令是先将cx减1再判断是否为零,而不是先判断cx是否为零再将cx减1,所以自己写的代码少循环了一次,少复制一个单元。学编程还是要多动手写,不要以为看懂,其实很多细节问题,写的时候才能发现。
    第3个问题其实也简单,将cs赋值给ds就可以了,cx循环次数的设置吗,先随便设置一个,准确的要经过debug看一下mov ax,4c00h前面有多少字节。百度了没有什么惊艳的方法,都是笨办法debug之后看前面有多少字节。之所以将这个问题拿处理说,是因为我本以为我发现了大家都没发现的办法,很high,最好发现原来是一场梦。最后一个问题,大家都知道主要就是确定循环次数cx,因为我隐约记得前面有一个知识点,是说某个寄存器保存了程序的长度,然后我翻出找出来了,第4章的4.9节里写了 “debug加载可执行文件后,cx中存放了程序的长度”,找到之后我就兴奋了,因为百度这个实验的时候发现大家用的都是笨办法,没人注意到这个细节,cx中存放了程序长度,顿时觉得我是天才,直接拿cx减去最后两条指令的长度,“sub cx,5”不就是要循环的次数吗。兴奋完了,再仔细看了看书,cx中显示程序长度的前提条件是要通过debug加载可执行文件,debug是调试程序的,我们正常执行程序都是通过shell(windows中就是command)来加载可执行文件的。然后就是测试,发现果然debug中我的设想能实现,正常通过shell执行,不能实现。我不是天才,大家都用笨办法是有原因的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值