(十一)《汇编语言(王爽)》 | 实验 7:寻址方式在结构化数据访问中的应用


1. 预备知识

  • 在 8086CPU 中,只有 bx、si、di、bp 可用在中括号内进行内存单元的寻址,如 mov ax,[ax] 错误;且这四个寄存器的两两组合只能是:bx 和 si、bx 和 di、bp 和 si、bp 和 di,其余组合方式均错误。

  • 寻址方式包括:

    • [idata] 直接寻址
    • [bx] 寄存器间接寻址
    • [bx+idata] 寄存器相对寻址
    • [bx+si] 基址变址寻址
    • [bx+si+idata] 相对基址变址寻址
  • 在 8086CPU 中提供的 [bx+si+idata] 的寻址方式为结构化数据的处理提供了方便:用 bx 定位整个结构体,用 idata 定位结构体中的某一数据项,用 si 定位数据项中的每个元素,上述寻址格式也可写作 [bx].idata[si]。

  • div 除法指令可以操作 8 位和 16 位两种数据:

    • 除数:有 8 位和 16 位两种,在一个寄存器或内存单元中
    • 被除数:默认放在 AX 或 DX 中,如果除数为 8 位则被除数为 16 位,默认在 AX 中存放;如果除数为 16 位则被除数为 32 位,在 AX 和 DX 中存放,DX 存放高 16 位、AX 存放低 16 位
    • 结果:如果除数为 8 位,则 AL 存储商、AH 存储余数;如果除数为 16 位,则 AX 存储商、DX 存储余数
  • 在没有寄存器参与的运算中,使用 X ptr 指明参与运算的数据长度,这在除法运算中经常使用,其中 X 为 word(16 字节)或 byte(8 字节)。

  • 伪指令 dd 用于定义双字类型

  • 重复指令 dup 和 db、dw、dd 等数据定义伪指令配合使用,用于进行数据的重复

dd 3 dup (0)		;定义了3个字节,它们的值都是0,相当于 db 0,0,0
dd 3 dup (0,1,2)	;相当于 db 0,1,2,0,1,2,0,1,2

2. 实验任务

下面是某公司从 1975 年成立一直到 1995 年的基本情况:

年份收入雇员人均收入
1975163?
1976227?
19773828?
1978135613?
1979239028?
1980800038?
1995593700017800?

下面程序已经完成数据的定义:

assume cs:codeseg
data segment	;数据段
	db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
	db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
	db '1993','1994','1995'
	;存放年份,每一项用4个字节表示
	dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
	;存放收入,每一项用4个字节表示
	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
	dw 11542,14430,15257,17800
	;存放雇员,每一项用2个字节表示
data ends
table segment	
	db 21 dup ('year summ ne ?? ')
	;表格段,年份(4)用year初始化、收入(4)用summ初始化、雇员(2)用ne初始化、人均收入(2)用??初始化
table ends

编程,将 data 段中的数据按如下格式写入 table 段中,并计算 21 年中的人均收入(取整)并保存到 table 段中:
请添加图片描述

  • 在 table 段中使用 dup 指令重复定义了 21 个字符串 "year summ ne ?? ",则内存空间 table:0~table:14FH 用于存放该公司 1975年~1995 年的基本情况。
  • 使用段寄存器 BX 和段寄存器 ES 分别指向数据段 data 和表格段 table。
mov ax,data
mov ds,ax				;段寄存器DS指向段data
mov ax,table		
mov es,ax 				;段寄存器ES指向段table
  • 首先处理表示年份的字符串,每一项用 4 个字节表示,可以连续使用两条 mov 语句完成,每次移动 2 个字节,形式为 mov es:[bp],ds:[bx]、mov es:[bp+2],dp:[bx+2]。
  • 处理完一项后,寄存器 BP 偏移 4 字节以指向数据段的下一项;寄存器 BX 偏移 16 字节以指向表格段的下一行。该部分代码为:
	mov bx,0
	mov bp,0
	mov cx,21
s:	mov ax,ds:[bx]
	mov es:[bp],ax 				;先移动前两个字节
	mov ax,ds:[bx+2]
	mov es:[bp+2],ax			;再移动后两个字节
	add bx,4					;每次偏移4个字节从data段中取数据
	add bp,16					;每次偏移16个字节写入下一行数据
	loop s
  • 其次处理表示收入的整数,使用 4 个字节表示。和处理年份时类似,使用两次移动完成,只是在从 data 中取数据时要加上存储年份区域的偏移,在存数据时也要加上相应的偏移。
  • 同样地,处理完一项后,寄存器 BP 偏移 4 字节以指向数据段的下一项;寄存器 BX 偏移 16 字节以指向表格段的下一行。该部分代码为:
	mov bx,0
	mov bp,0
	mov cx,21
s:	mov ax,ds:[84+bx]		;84是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+5],ax 		;先移动前两个字节
	mov ax,ds:[84+bx+2]
	mov es:[bp+5+2],ax		;再移动后两个字节
	add bx,4				;每次偏移4个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s
  • 然后处理雇员这一项,经过前面两项的处理后,这一项的处理类似。该部分代码为:
	mov bx,0
	mov bp,0
	mov cx,21	
s:	mov ax,ds:[168+bx]		;168是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+10],ax		;10是表格段中存放收入内存相对于存放年份的偏移,每次处理两个字节
	add bx,2				;每次偏移2个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s
  • 由于人均收入等于收入除以雇员数,根据预备知识部分,除数(雇员数)为 16 为则被除数(收入)为 32 位,且 DX 存放高 16 位,AX 存放低 16 位。此时结果的存放情况为:AX 存储商,DX 存储余数,由于题目只使用 16 位存储结果,所以忽略余数的处理,所以除法结果存放在寄存器 AX 中。
  • 根据前面的程序,被除数(收入)的低位存放在 [84+bx] 的内存单元中,高位存放在 [84+bx+2] 的内存单元中(由下图可知,存放形式为小端序);除数(雇员)存放在 [bx+168] 中。
  • 同时,使用 word ptr 指明每次除法运算时参与计算的数值长度为 16 位。该部分代码为:
	mov bx,0
	mov bp,0
	mov si,0
	mov cx,21
s:	mov ax,ds:[84+bx]		;AX存放低16位
	mov dx,ds:[84+bx+2]		;DX存放高16位
	div word ptr ds:[168+si];word ptr指定除法运算的数值为16位,且结果存放在AX中(不考虑余数)
	mov es:[bp+13],ax		;存入表格中
	add bx,4				;每次偏移4个字节从data段中取被除数数据
	add si,2				;每次偏移2个字节从data段中取除数数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s

至此已经完成全部功能,将代码整合:

assume cs:codeseg
data segment	;数据段
	db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
	db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
	db '1993','1994','1995'
	;存放年份,每一项用4个字节表示
	dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
	;存放收入,每一项用4个字节表示
	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
	dw 11542,14430,15257,17800
	;存放雇员,每一项用2个字节表示
data ends
table segment	
	db 21 dup ('year summ ne ?? ')
	;表格段,年份(4)用year初始化、收入(4)用summ初始化、雇员(2)用ne初始化、人均收入(2)用??初始化
table ends

codeseg segment
start:
	mov ax,data
	mov ds,ax				;段寄存器DS指向段data
	mov ax,table		
	mov es,ax 				;段寄存器ES指向段table
	
	mov bx,0
	mov bp,0
	mov cx,21
s1:	mov ax,ds:[bx]
	mov es:[bp],ax 			;先移动前两个字节
	mov ax,ds:[bx+2]
	mov es:[bp+2],ax		;再移动后两个字节
	add bx,4				;每次偏移4个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s1
	
	mov bx,0
	mov bp,0
	mov cx,21
s2:	mov ax,ds:[84+bx]		;96是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+5],ax 		;5是表格段中存放收入内存相对于存放年份的偏移,先移动前两个字节
	mov ax,ds:[84+bx+2]
	mov es:[bp+5+2],ax		;再移动后两个字节
	add bx,4				;每次偏移4个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s2
	
	mov bx,0
	mov bp,0
	mov cx,21	
s3:	mov ax,ds:[168+bx]		;168是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+10],ax		;10是表格段中存放收入内存相对于存放年份的偏移,每次处理两个字节
	add bx,2				;每次偏移2个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s3
	
	mov bx,0
	mov bp,0
	mov si,0
	mov cx,21
s4:	mov ax,ds:[84+bx]		;AX存放低16位
	mov dx,ds:[84+bx+2]		;DX存放高16位
	div word ptr ds:[168+si];word ptr指定除法运算的数值为16位,且结果存放在AX中(不考虑余数)
	mov es:[bp+13],ax		;存入表格中
	add bx,4				;每次偏移4个字节从data段中取被除数数据
	add si,2				;每次偏移2个字节从data段中取除数数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s4
	
	mov ax,4c00h
	int 21h
codeseg ends
end start

使用 DEBUG 查看运行前数据段的内容。红色为存储年份的区域,蓝色为存储收入的区域,绿色为存储雇员的区域,白色为填充区域。

请添加图片描述
使用 DEBUG 查看运行前后表格段的内容:

请添加图片描述
如图,红色为存储年份的区域,蓝色为存储收入的区域,绿色为存储雇员的区域,白色存储人均收入的区域:

请添加图片描述

上述代码使用 4 个循环完成,可以对部分循环进行嵌套以简化代码:

  • 最外层循环 21 次,这个基本部分不变。
  • 在上述循环中,寄存器 BX 的值每次偏移 2 或 4 个字节,所以需使用两个寄存器分别表示,这里分别使用 SI 和 DI。
assume cs:codeseg
data segment	;数据段
	db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
	db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
	db '1993','1994','1995'
	;存放年份,每一项用4个字节表示
	dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
	;存放收入,每一项用4个字节表示
	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
	dw 11542,14430,15257,17800
	;存放雇员,每一项用2个字节表示
data ends
table segment	
	db 21 dup ('year summ ne ?? ')
	;表格段,年份(4)用year初始化、收入(4)用summ初始化、雇员(2)用ne初始化、人均收入(2)用??初始化
table ends

codeseg segment
start:
	mov ax,data
	mov ds,ax				;段寄存器DS指向段data
	mov ax,table		
	mov es,ax 				;段寄存器ES指向段table
	
	mov si,0
	mov di,0
	mov bp,0
	mov cx,21
s:	mov ax,ds:[si]
	mov es:[bp],ax 			;先移动前两个字节
	mov ax,ds:[si+2]
	mov es:[bp+2],ax		;再移动后两个字节

	mov ax,ds:[84+si]		;96是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+5],ax 		;5是表格段中存放收入内存相对于存放年份的偏移,先移动前两个字节
	mov ax,ds:[84+si+2]
	mov es:[bp+5+2],ax		;再移动后两个字节

	mov ax,ds:[168+di]		;168是数据段中存放收入内存相对于存放年份的偏移	
	mov es:[bp+10],ax		;10是表格段中存放收入内存相对于存放年份的偏移,每次处理两个字节

	mov ax,ds:[84+si]		;AX存放低16位
	mov dx,ds:[84+si+2]		;DX存放高16位
	div word ptr ds:[168+di];word ptr指定除法运算的数值为16位,且结果存放在AX中(不考虑余数)
	mov es:[bp+13],ax		;存入表格中
	
	add si,4				;每次偏移4个字节从data段中取数据
	add di,2				;每次偏移2个字节从data段中取数据
	add bp,16				;每次偏移16个字节写入下一行数据
	loop s
	
	mov ax,4c00h
	int 21h
codeseg ends
end start

结果和第一部分相同。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值