本博文系列参考自<<汇编语言>>第三版,作者:王爽
1.[bx]和内存单元的描述
[bx]与我们前面见过的[0]类似,mov ax,[0] 的意思是将内存地址为DS:0的两字节内容存入ax中。其中[0]中的0代表的是偏移地址。
类似的,我们有 mov al,[0]的意思是将内存地址为DS:0的单字节内容存入al中。那么我们可以大胆的推断mov ax,[bx]代表的是将偏移地址为bx寄存器中的值的内存地址的两字节内容存入到ax中,其段地址在DS中存储。
2.关于定义的描述性符号: "()"
后面的内容我们都将用符号"()"来表示一个内存单元或一个寄存器中的值。(ax)表示寄存器ax中的值,(al)表示寄存器al中的值。
(20000H)表示内存地址为20000的内容
((ds)*16+(bx)) ds的内容为R1,bx中的地址为R2。那么该公式代表R1*16+R2内存地址处的内容。
"( )"中可以有三种类型元素:(1).寄存器 (2)段寄存器 (3)内存地址 其表示的数据有两种类型:字和字节。如何区分数据类型是根据寄存器类型和运算类型进行区分的。
"()"的应用举例:
1.[bx]
看几个指令:
mov ax,[bx] 设偏移地址为SA,段地址默认存储在DS寄存器中,那么该指令的作用是将地址为DS:SA处的值传入ax寄存器中。
用"()"符号解释为 (ax)=((DS)*16+(bx))
mov [bx],ax 设偏移地址为SA,段地址默认存储在DS寄存器中,那么该指令的作用是将寄存器ax值传入[bx]所代表的偏移地址的位置,用"()"符号解释为 ((DS)*16+(bx))=(ax)
2. LOOP指令
LOOP指令为循环指令,其格式为 LOOP 标号,当cx不为0的时候跳转到标号处循环同时cx=cx-1,如果标号为0则向下执行。(CX默认为循环计数器)
下面实现一个小程序,用LOOP实现2^12,代码如下:
1.标号
标号s处有一条指令,add ax,ax用于累加,标号s代表该条指令的地址。
2.loop s
当cx寄存器中的值不为0的时候将跳转到标号处循环执行add ax,ax。当cx=0的时候,loop s不再跳转,将执行Loops后面的指令。
用cx和loop实现循环框架如下:
mov cx,循环次数
标号: 代码段
loop 标号
现在有这样一个程序功能需要实现:计算ffff:0006单元中的数乘以3,结果存放在dx中。
此程序有三个注意点
1、ffff:0006单元中的数是一个字节型数据。范围为0-255,乘以3完全可以放到dx中,不用担心超过dx的范围
2、将ffff:0006单元中的数赋值给ax,然后令(dx)=0,用dx存储累加三次的结果
3、因为ffff:0006为一个字节单元,ax为一个字单元。如何将ffff:0006单元的值赋值给ax呢。这里只需要将ax中高位字节扩展为
00h即可,比如ffff:0006中为2CH,那么赋值给ax后为002CH,虽然数据长度不等,但是值是相等的。
注意几点,0ffffh为何前面会有个"0",当16进制数据最高位为字母的时候需要在最高位前面加上0.
下面我们通过masm和link两条指令对该程序进行编译链接生成exe文件。
用debug工具加载vpoet.exe:
DS为14E9 CS:IP为 14F9:0000 指向第一条指令 mov AX,FFFF
现在我们用U指令查看程序内容。
可以看到LOOP S处的地址为14F9:E2FC 此时LOOP S中的S标号变成地址0012,当执行了LOOP循环后,那么CX=CX-1,如果CX不为0,那么将IP设置为S标号的地址0012 CPU将执行地址14F9:0012处的指令ADD
DX,AX实现叠加。
前三条指令执行完之后,DS被设置为FFFFH,BX设置为0006H 此时DS:bx指向 FFFF:6,接下来将执行MOV AL,[BX]取出FFFF:6地址处的值,在右边显示了该值为32H
继续执行下面两条指令:
执行完后完成了对AX的复制,AX值为0032H
继续执行 MOV DX,0和MOV CX,3完成累加器和计数器的初始化
下面开始循环过程,可以看出每次循环一次CX减小1,同时DX增加一倍。
当CX=0000时候循环结束,执行接下来的返回语句 MOV AX,4c00h和int 21h。至此整个程序结束将CPU控制器交给command.
5.4 Debug和汇编编译器masm对指令的不同处理
1.在汇编语言中,如果一个指令要访问内存单元,则在指令中必须用[...]来表示内存单元,如果在[...]中直接用常量给出内存地址的偏移地址,那么需要在"[ ]"前面加上段寄存器。比如
mov al,ds:[0]如果没有显示的给出段寄存器ds:,那么mov al,[0]在汇编语言中被理解为mov al,0
2.在汇编语言中,也可以将偏移地址存入一个普通寄存器bx,比如
mov bx,0
mov al,[bx]这样也是允许的,其中段寄存器默认在ds中
5.5 段前缀
汇编指令mov ax,[bx]将偏移地址在寄存器bx中指出,而段地址默认在ds中,我们也可以在指令中显示的给出段地址:
比如
mov ax,ds:[bx]
mov ax,ds:[bx]
mov ax,cs:[bx]
mov ax,ds:[0]
mov ax,ds:[0]
mov ax,cs:[0]
其中 ds: cs: ss:称为段前缀
5.6 一段安全的空间
在汇编中,当我们更改某个内存地址的内容的时候需要十分谨慎,因为该地址可能存储着重要的代码或者数据,一旦覆盖或者更改会导致整个系统的崩溃。在DOS方式下,DOS和其他合法的程序一般都不会使用0:200~0:2ff这256个字节的空间。所以,使用这段空间是安全的,我们可以在debug下查看这段地址是否为0,如果为0证明该内存段未被DOS和其他程序使用。
5.8段前缀的使用
问题,我们需要将内存ffff:0~ffff:b单元的数据复制到0:200~0:20b
分析如下:
1.0:200~0:20b等同于 0020:0~0020:b,这样描述为了使得目标地址和源地址偏移量都从0开始
2.复制过程用loop循环实现
初始化:
X=0
循环12次:
将ffff:X单元的数据送入0020:X(需要一个寄存器中转)
X=X+1
3.循环中ffff:X和0200:X的偏移量X存入bx
代码及注释如下:
OK,only stop here~~