汇编语言(九)
第八章 数据处理的两个基本问题
我们用描述性的符号reg表示寄存器,用sreg表示段寄存器。
1 | reg: ax,bx,cx,dx,ah,al,bh,bl,ch,cl,dh,dl,sp,bp,si,di |
bx,si,di,bp
只有这4个寄存器可以用在[]中进行内存单元寻址。比如
1 | mov ax,[bx+si] |
在[]中这4种寄存器可以单个出现,如果组合出现,只能出现4种组合,即
1 | [bx+si],[bx+di],[bp+si],[bp+di]其他都是错误的。 |
如果[]中使用bp但是指令中没有给出段地址,则段地址默认在ss中。
比如:
1 | mov ax,[bp]含义:(ax) = ((ss)*16+(bp)) |
机器指令处理的数据在什么地方
大部分机器指令都是进行数据处理的指令,分为三类:读取,写入,运算。
机器指令并不关心数据的值,而是关心指令执行前一刻,将要处理的数据所在的位置。
一般所处理的数据出现在3个地方:CPU内部,内存,端口。
举例:
1 | mov bx,[0] 内存,ds:0单元 |
汇编语言中数据位置的表达
1.立即数(idata)
这是直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中)
例:
1 | mov ax,1 |
2.寄存器
给出相应寄存器名。
1 | mov ax,bx |
3.段地址(SA)和偏移地址(EA)
指令要处理的数据在内存中,在汇编指令中可用[X]的格式给出EA,SA在某个段寄存器中(段寄存器可能为默认)
1 | mov ax,[bx+8]#带有bx的段地址默认在ds中 |
1 | mov ax [bp+si]#带有bp的段地址默认在ss中 |
也可以显性给出段寄存器地址比如
1 | mov ax,ds:[bp] |
寻址方式
由此图可以看出只有寄存器bp对应的段寄存器为ss,其他都是ds。后面四个都是寄存器间接寻址,因为段寄存器都默认了,偏移地址为bx,si等寄存器内部的值。
下面是各种我们讲过的寻址方式的总结:
指令要处理的数据有多长
我们要知道指令进行的是字操作还是字节操作。
1.可以看寄存器名指明处理的数据尺寸
1 | mov ax,1 |
显然这是字操作。一次动了两个字节。
然而下面就是字节操作了:
1 | mov al,1 |
2.没有寄存器名存在的情况,用操作符X ptr指明内存单元的长度这个X可以是字或字节
比如
1 | mov word ptr ds:[0],1 |
再来看
1 | mov byte ptr ds:[0],1 |
没有寄存器参与的内存单元访问指令中,用word ptr或者byte ptr指明所要访问的内存单元的长度是很有必要的。
我们举个例子吧:
对于
1 | mov ax,2000h |
显然变换之后内存中数据为
只变了一个字节,然而我们知道,如果不指明只变了一个字节的话,会一下变一个字。
也就是代码
1 | mov ax,2000h |
显然这样就会变为
注:push [1000h]就不用指明字或字节,因为push只能进行字操作!
寻址方式的综合应用
来看一个实际问题:
这些数据在下图中这样存放
所以我们要修改排名字段,收入字段和产品字段的第一,第二和第三个字符。
如下是具体的步骤!
我打出程序的代码:
1 | mov ax,seg |
下面给出一个C语言示例代码:
1 | struct company{ |
下面按照C语言风格用汇编去改一下:
1 | mov ax,seg |
我们可以看出数组中找到每个元素可以写成
1 | [bx].idata[si] |
div指令
除法指令
1.除数有8和16两种,在一个reg或者内存单元中。
2.被除数默认在AX或DX和AX中,如果除数为8位,被除数则为16位,默认在AX中存放;如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。(不太好记)
3.结果:如果除数为8位,则AL存储商,AH存储余数;如果除数为16位,则AX存储商,DX存储余数。
我总结了一下上述的特点,方便记忆:小的就只存AX,大的就存AX和DX但是先AX后DX,比如被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
1 | div byte ptr ds:[0]#存放除数,只需要指明除数即可,因为被除数与商都在AX中,这里除数为8位,所以都是AX |
下面看除数是16位的
1 | div word ptr es:[0]#显然这里除数是16位的,被除数32位高放DX |
下面来练习一个题目:
编程,利用除法指令计算100001/100
首先被除数100001>65535=2^16不能用ax单独存放,所以除数为16位(被除数大于16位了)
除数100<255可以放在8位寄存器中,但是这里要用16位寄存器存放除数100。
代码如下:
1 | mov dx,1 |
可以自行执行。
再来练习一道利用除法计算1001/100
1 | 显然1001是16位以内,100是8位以内,所以被除数和除数分别为16位和8位。很正规 |
程序如下:
1 | mov ax,1001 |
伪指令
我们用db和dw来定义字节型数据和字型数据。dd是用来定义dword(double word,双字)数据的。
比如:
1 | data segment |
显然定义了3个数据
第一个数据为01h,在data:0处,占一个字节。
第二个数据为0001h,在data:1处,占一个字。
第三个数据为00000001h,在data:3处,占2个字。
1 | data segment |
解决代码如下:
1 | mov ax,data |
dup
这是一个操作符,同db,dw,dd一样,与db,dw,dd等伪指令配合使用进行数据的重复。比如
1 | db 3 dup (0) |
相当于
1 | db 0,0,0定义了三个字节,他们的值都是0! |
1 | db 3 dup (0,1,2)#即三个0,1,2 |
定义了9个字节,它们是0,1,2,0,1,2,0,1,2相当于
1 | db 0,1,2,0,1,2,0,1,2 |
再来一个
1 | db 3 dup ('abc','ABC') |
一般用来定义长栈段
比如
1 | stack segment |