8086汇编 实验7:寻址方式在结构化数据访问中的应用

原题

在这里插入图片描述
下面程序中,已经定义好了这些数据:

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'

	dd 16,22,382,1356,2390,8000,16000,24486,50056,97479,140417,197514
	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000

	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
	dw 11542,14430,15257,17800
data ends

table segment
	db 21 dup ('year summ ne ?? ')
table ends

编程将data段中的数据按如下格式写入到table段中,并计算21年中的人均收入(取整),结果也按照下面的格式保存在table段中:
在这里插入图片描述
在这里插入图片描述

解题

C语言实现

首先我们理解题意,用我们熟悉的C语言描述一下需要进行的操作:

  • 首先是data数据段,在C语言中,用数组来存储:
char years[21][4] = {'1', '9', '7', '5', '1', '9', '7', '6',
					 '1', '9', '7', '7', '1', '9', '7', '8',
					 '1', '9', '7', '9', '1', '9', '8', '0',
					 '1', '9', '8', '1', '1', '9', '8', '2',
					 '1', '9', '8', '3', '1', '9', '8', '4',
					 '1', '9', '8', '5', '1', '9', '8', '6',
					 '1', '9', '8', '7', '1', '9', '8', '8',
					 '1', '9', '8', '9', '1', '9', '9', '0',
					 '1', '9', '9', '1', '1', '9', '9', '2',
					 '1', '9', '9', '3', '1', '9', '9', '4',
					 '1', '9', '9', '5'};
int summs[21] = {16, 22, 382, 1356, 2390, 8000, 16000, 24486,
				 50056, 97479, 140417, 197514, 345980, 590827,
				 803530, 1183000, 1843000, 2759000, 3753000, 4649000, 5937000};
short nes[21] = {3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001,
				 1442, 2258, 2793, 4037, 5635, 8226, 11542, 14430, 15257, 17800}; 
  • 然后我们要建立一个table结构体:
struct table
{
	char year[4];
	int summ;
	short ne;
	short ave;
};
struct table T[21];
  • 现在题目要我们做的就是将data段的数据存入table段,用C语言实现就是:
// C语言描述
int main()
{
	for (int i = 0; i < 21; i++)
	{
		for (int j = 0; j < 4; j++)
			T[i].year[j] = years[i][j];
		T[i].ne = nes[i];
		T[i].summ = summs[i];
		T[i].ave = summs[i] / nes[i];
	}
}
  • 下面我们分别输出table表的10进制形式和16进制形式(用作后面的比较):
	for (int i = 0; i < 21; i++)
	{
		for (int j = 0; j < 4; j++)
			printf("%c", T[i].year[j]);
		printf("\t%d", T[i].summ);
		printf("\t%d", T[i].ne);
		printf("\t%d\n", T[i].ave);
	}
	for (int i = 0; i < 21; i++)
	{
		for (int j = 0; j < 4; j++)
			printf("%c", T[i].year[j]);
		printf(" %08x", T[i].summ);
		printf(" %04x", T[i].ne);
		printf(" %04x\n", T[i].ave);
	}

运行结果为:
在这里插入图片描述
在这里插入图片描述

8086汇编实现

有了以上的分析,下面用汇编代码来实现上面的功能
上面的C语言描述可知,我们要将内存中的一段数据,按照一定的规律复制到内存的另一块区域,也就是cpu先读取内存的数据,然后写入数据到内存,写入前还会有数据的计算。

  • 在代码段前还需要一个栈段来帮助处理数据,C语言描述中有双层循环需要用到栈
stacksg segment
	db 16 dup (0)		;16字节的内存作为栈段
stacksg ends
  • 对两块内存区域操作,可以用到dses两个段寄存器,使ds为数据data段的段寄存器,es为table段的段寄存器,下面一段代码先将dsesss三个段寄存器分别存入数据:
start:	mov ax,stacksg
		mov ss,ax
		mov ax,datasg
		mov ds,ax
		mov ax,tablesg
		mov es,ax
  • 下面代码使用了bp来单独对table段进行寻址,每次循环自增16(因为一个table结构体实例的大小是16字节);使用di来对data段的ne数据进行寻址,每次循环自增2(每个ne数据占用两个字节);使用si来对data段的summ数据进行寻址,每次循环自增4(summ为双字型数据,占4个字节);但我们发现year数据也是4个字节,那么同时可以将si看做是对每个年份的寻址,在利用bx对每个year数据循环遍历每一个字节;在计算除法时,注意是32位数除以16位数,需要axdx两个寄存器。
    完整汇编代码:
assume cs:codesg,ds:datasg,ds:tablesg,ss:stacksg

stacksg segment
	db 16 dup (0)		;16字节的内存作为栈段
stacksg ends

datasg 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'
	;以上是表示21年的21个字符串,起始地址datasg:0

	dd 16,22,382,1356,2390,8000,16000,24486,50056,97479,140417,197514
	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
	;以上是表示21年公司收入的21个dword型数据,起始地址datasg:84

	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
	dw 11542,14430,15257,17800
	;以上是表示21年公司雇员人数的21个word新数据,起始地址datasg:168
	;数据段总大小210字节
datasg ends

tablesg segment
	db 21 dup ('year summ ne ?? ')
				;|0123456789abcdef|
				;|year summ ne ?? |
				; 0    5 7  a  d
tablesg ends

codesg segment

start:	mov ax,stacksg
		mov ss,ax
		mov ax,datasg
		mov ds,ax
		mov ax,tablesg
		mov es,ax

		mov bp,0 				;用于table表寻址,每次循环自增16
		mov si,0 				;用于数据中summ寻址,每次循环自增4
		mov di,0 				;用于数据中ne寻址,每次循环自增2
		mov cx,21
	s:	push cx

		mov bx,si  				;用于数据中year寻址
		push si
		mov si,0
		mov cx,4
	s0:	mov al,ds:[bx+si]
		mov es:[bp+si],al
		inc si
		loop s0
		pop si
								;以上循环实现将年份信息存入table表中

		mov ax,ds:[di+168]
		mov es:[bp+0ah],ax		;table表存入ne
								;C语言描述中的T[i].ne = nes[i]

		mov ax,ds:[si+84]		;双字型数据的低16位
		mov dx,ds:[si+86]		;双字型数据的高16位
		mov es:[bp+5],ax
		mov es:[bp+7],dx		;table表存入summ
								;C语言描述中的T[i].summ = summs[i]

		div word ptr es:[bp+0ah]
		mov es:[bp+0dh],ax		;除法计算,table表存入ave
								;C语言描述中的T[i].ave = summs[i] / nes[i];

		add bp,16
		add si,4
		add di,2
		pop cx
		loop s

		mov ax,4c00h
		int 21h

codesg ends

end start

测试

首先进入dosbox输入masm指令编译:
在这里插入图片描述
没有错误,输入指令link链接:
在这里插入图片描述
看到生成的exe文件后就可以进行调试了
在这里插入图片描述
debug进行调试
在这里插入图片描述
u命令找到程序结束时的cs:ip
在这里插入图片描述
输入g 04d2:0058使程序运行完成
在这里插入图片描述
先用d命令查看data数据段在内存中的存储形式
在这里插入图片描述
发现数据存储的连续性
下面d命令查看es段寄存器区域的table表的写入情况
在这里插入图片描述
可以看到该段数据已经被写入数据,右边是ASCII码对应的字符,左边是16进制数,下图是个数据的表示区域
在这里插入图片描述
在这里插入图片描述
数据对照可以发现汇编程序的运算结果是正确的

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值