原题
下面程序中,已经定义好了这些数据:
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
- 对两块内存区域操作,可以用到
ds
,es
两个段寄存器,使ds
为数据data段的段寄存器,es
为table段的段寄存器,下面一段代码先将ds
,es
,ss
三个段寄存器分别存入数据:
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位数,需要ax
,dx
两个寄存器。
完整汇编代码:
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进制数,下图是个数据的表示区域
数据对照可以发现汇编程序的运算结果是正确的