第八章 数据处理的两个基本问题
本章对前面的所有内容是具有总结性的
计算机是进行数据处理、运算的机器,那么有两个基本的问题就包含在其中:
1.处理的数据在什么地方?
2.要处理的数据有多长?
这两个问题,在机器指令中必须给以明确或隐含的说明,否则计算机就无法工作
8.1 bx、si、di、bp
1.在8086CPU中,只有这4个寄存器(bx、bp、si、di)可以用在“[…]”
中,用来进行内存单元的寻址
2.在“[…]”中,这四个寄存器(bx、bp、si、di)可以单个出现,
或者只能以以下4种组合出现
1.bx和si
2.bx和di
3.bp和si
4.bp和di
3.错误的用法
mov ax,[bx+bp]
mov ax,[si+di]
4.只要在[…]中使用寄存器bp(帮sp不够用的时候),则指令中没有显性给出段地址,那么
段地址就默认在ss中,比如:
mov ax,[bp] ax的值为栈空间中,偏移地址为bp的内存单元
mov ax,[bp+常数]
mov ax,[bp+si]
mov ax,[bp+si+常数]
8.2 机器指令处理的数据所在的位置
1.绝大部分机器指令进行数据处理的指令大致可分为3大类
读取、写入、运算
2.在机器指令这一层,并不关心数据的值是多少,而关心指令执行前一刻
它将要处理的数据所在的位置
3.指令在执行前,所要处理的数据可以在三个地方
CPU内部(寄存器)、内存、端口(后面讲)
8.3 汇编语言中数据位置的表达
汇编语言中用三个概念来表达数据的位置
1.立即数(idata)immediate data
对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),在汇编语言中称为:立即数(idata),在汇编指令中直接给出。
例:mov ax,1
2.寄存器
指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。例:mov ax,bx
3.段地址(SA)和偏移地址(EA)
1.存放段地址的寄存器可以是默认的,
既可以是默认在ds中,也可以是在ss中(使用bp寄存器)
2.存放段地址的寄存器也可以显性的给出
mov ax,ds:[bp]
mov ax,es:[bx]
mov ax,ss:[bx+si]
mov ax,cs:[bx+si+8]
8.4 寻址方式
8.5 指令要处理的数据有多长?
1.8086CPU的指令,可以处理两种尺寸的数据,byte和word
所以在机器指令中要指明,指令进行的是字操作还是字节操作
2.8086CPU确定数据长度的几种方法
1.通过寄存器名指明要处理的数据的尺寸
mov al,1 ;指明数据是字节型的
mov bx,ds:[0] ;指明数据是字型的
2.在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度
X在汇编指令中可以为word或byte
1.下面的指令中,用byte ptr指明了指令访问的内存单元是字节型单元
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2
2.下面的指令中,用word ptr指明了指令访问的内存单元是字型单元
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
在没有寄存器参与的内存单元访问指令中,用word ptr或者byte ptr,显性地指明所要访问的内存单元的长度,是非常有必须要的。否则,CPU无法得知所要访问的单元是字单元,还是字节单元
假设我们用Debug查看内存的结果如下:
2000:1000 FF FF FF FF FF FF…
那么指令:
mov ax,2000H
mov ds , ax
mov byte ptr [1000H],1
将使内存中的内容变为:
2000: 1000 01 FF FF FF FF FF …
而指令:
mov ax, 2000H
mov ds , ax
mov word ptr [ 1000H],1
将使内存中的内容变为:
2000: 1000 01 00 FF FF FF FF…
这是因为mov byte ptr [1000H],1访问的是地址为ds:1000H的字节单元,修改的是ds:1000H单元的内容;而mov word ptr [1000H],1访问的是地址为ds:1000H的字单元,修改的是 ds:1000H 和 ds:1001H两个单元的内容。
3.其他方法
有些指令默认了访问的是字单元还是字节单元,比如,push [1000H]就 不用指明访问的是字单元还是字节单元,因为push指令只进行字操作。
sp = sp-2
8.6 寻址方式的综合应用
以上是该公司1982年的情况,到了1988年 DEC公司的信息有了如下变化。(1) Ken Olsen在富翁榜上的排名已升至38位;
(2)DEC的收入增加了70亿美元;
(3)该公司的著名产品已变为VAX系列计算机。我们提出的任务是,编程修改内存中的过时数据。首先,我们应该分析一下要修改的数据。
要修改内容是:
(1)(DEC公司记录)的(排名字段)(2)(DEC公司记录)的(收入字段)
(3)(DEC公司记录)的(产品字段)的(第一个字符)、(第二个字符)、(第三个字符)从要修改的内容,我们就可以逐步地确定修改的方法。
(1)要访问的数据是DEC公司的记录,所以,首先要确定DEC公司记录的位置:R=seg:60
确定了公司记录的位置后,下面就进一步确定要访问的内容在记录中的位置。(2)确定排名字段在记录中的位置:0CH。
(3)修改R+0CH处的数据。
(4)确定收入字段在记录中的位置:OEH。
(5)修改R+0EH 处的数据。
(6)确定产品字段在记录中的位置:10H。
要修改的产品字段是一个字符串(或一个数组),需要访问字符串中的每一个字符。所以要进一步确定每一个字符在字符串中的位置。
(7)确定第一个字符在产品字段中的位置:P=O。(8)修改R+10H+P处的数据;P=P+1。
(9)修改R+10H+P处的数据;P-P+1。
(10)修改 R+10H+P处的数据。根据上面的分析,程序如下。
mov ax, seg
mov ds, ax
mov bx,60h;确定记录地址,ds:bx
mov word ptr [bx+0ch],38;排名字段改为38
add word ptr [bx+0eh],70;收入字段增加70
mov si,0;用si来定位产品字符串中的字符
mov byte ptr [bx+10h+si],'v'
inc si
mov byte ptr [bx+10h+si],'A'
inc si
mov byte ptr [bx+10h+si],'x'
8.7 div指令
1.div是除法指令(division),使用div作除法的时候,要求
1.除数:8位或16位,在寄存器或内存单元中
2.被除数:(默认)放在 AX 或 DX 和AX 中
3.除数与被除数的相互关系
除数 被除数
8位 16位(AX)
16位 32位(DX+AX)
4.结果存放的位置
运算 8位 16位
商 AL AX
余数 AH DX
2.div指令格式
1.div 寄存器
2.div 内存单元
除数是寄存器或内存单元的内容
3.div指令示例
1.div byte ptr ds:[0] ;被除数是16位,除数是ds:[0]的内容(8位)
含义:(al)=(ax)/((ds)*16+0)的商
(ah)=(ax)/((ds)*16+0)的余数
2.div word ptr es:[0] ;被除数是32位,除数是es:[0]的内容(16位)
含义:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商
(dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余数
4.利用除法指令计算100001/100
1.被除数100001大于65535,要使用dx和ax两个寄存器联合存放
即说要进行的16位的除法
2.除数100小于255,可以在一个8位寄存器中存放,但是,因为被除数是32位,除数应为16位,所以要用16位寄存器来存放除数100
3.现将100001表示成十六进制数:186A1H,即dx中存放1H,ax中存放86A1H
mov dx,1
mov ax,86A1H ;(dx)*10000H+(ax)=100001
mov bx,100
div bx ;默认除数是16位的
;程序执行后 ax = 03e8h(1000 商),dx = 1(余数)
5.编程,利用除法指令计算1001/100。
首先分析一下,被除数1001可用ax寄存器存放,除数100可用8位寄存器存放,也就是说,要进行8位的除法。程序如下。
mov ax,1001
mov bl,100
div bl
程序执行后,(al)=OAH(即 10),(ah)=1(余数为1)。读者可自行在Debug 中实践。
8.8 伪指令dd
1. dd是用来定义双字型数据的 define double word
2.示例
data segment
db 1 ;字节型数据 一个字节,01H,在data:0处
dw 1 ;字型数据 一个字,0001H,在data:1处
dd 1 ;双字型数据 2个字 00000001H,在data:3处
data ends
3.已知data段数据,用div计算data中第一个数据除以第二个数据后的结果,商存放在第3个数据的内存单元中
assume cs:code,ds:data
data segment
dd 100001
dw 100
dw 0
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov ax,[bx] ;低位存放在ax中
mov dx,[bx+2] ;高位存放在dx中
div word ptr [bx+4]
mov [bx+6],ax ;商存放在ax中,把ax中的内容放入内存中
mov ax,4c00h
int 21h
code ends
end start
8.9 dup
1.dup是一个操作符,在汇编语言中,同db、dw、dd等一样,也是有编译器识别处理的符号
2.dup和db、dw、dd等数据定义伪指令配合使用的,用来进行数据的重复
3.dup示例
1.db 3 dup(0) ;定义了3个字节,他们的值都是0
2.db 3 dup(0,1,2) ;定义了9个字节,他们是0、1、2、0、1、2、0、1、2
3.db 3 dup(‘abc’,‘ABC’) ;定义了18个字节,相当于db ‘abcABCabcABCabcABC’
4.dup的使用格式
db 重复的次数 dup(重复的字节型数据)
dw 重复的次数 dup(重复的字型数据)
dd 重复的次数 dup(重复的双字型数据)
【实验七】寻址方式在结构化数据访问中的应用
assume cs:code,ds:data,ss:stack,es:table
stack segment
dw 8 dup(0) ;空栈时,sp指向16
stack ends
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983' ;起始地址0,终止地址21*4-1:83 (0-53H)
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;表示21年的21个字符串(4byte)
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514;起始地址21*4:84,终止地址21*4+21*4-1:167 (54H-0A7H)
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;表示21年公司总收入的21个双字型(4byte)数据
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226;起止地址21*8:168,终止地址21*8+21*2-1:209 (0A8H-0D1H)
dw 11542,14430,15257,17800
;表示21年公司雇员人数的21个字型数据(2byte)
data ends
table segment
db 21 dup('year summ ne ?? ') ;
table ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
mov ax,stack
mov ss,ax
mov sp,16
mov si,0
mov di,0
mov bx,0
mov bp,0
mov cx,21
outer:
push si
add si,si
mov ax,ds:[bp]
mov es:[bx][di],ax
mov ax,ds:84[bp]
mov es:[bx][di+5],ax
pop si
mov al,168[si]
mov es:[bx][di+10],al
inc si
add di,2
push si
add si,si
mov ax,ds:[bp]
mov es:[bx][di],ax
mov ax,ds:84[bp]
mov es:[bx][di+5],ax
pop si
mov al,168[si]
mov es:[bx][di+10],al
inc si
add di,2
add bx,16
loop outer
mov ax,4c00h
int 21h
code ends
end start
一直到78E0h:0才是代码段的位置,前面是数据。cx是21
076aH:0 是数据data段ds的位置。0778d:0是table开始es的位置
最后得到的结果和题目排列要求一样
验证平均收入数是正确的