话不多说,上图(原题为王爽老师的汇编语言P161):
Solution
如果用现在的高级语言来完成本题可以说是小菜一碟,但如果是只能使用几个简单的寄存器的和一些基础得不行的汇编语言,笔者作为初学者,认为这个还是有难度的。
如何解决此问题?乍一看这些数据真的是让人眼花缭乱,题中给出的格式更是令本人摸不着头脑,但在一步一步地分析之后,有着一定的高级语言编程基础,便有了这样的感觉: 题中给出的数据段(data segment)分为三大块:
- 首先是字节型数据,每个元素占 1 个字节,与C语言中的字符类型char相似,如 ‘1975’ 等价于 ‘1’, ‘9’, ‘7’, ‘5’ 四个字符。
- 第二块是double word 双字类型,每个元素占 4 字节(32 bits),可以类比于C语言中的有符号整型 (signed int 类型)。
- 第三个类似于第二块数据,同样是整型数据,字(word)类型限制了每个元素的空间为 2 字节。
这些排列整齐,并且每块都是相同类型的元素也就是高级语言中数组的雏形。
如何访问数组元素?
C语言中我们使用如 arr[ i ] 的方式对每个元素进行访问,在汇编当中有与之相似的方法: address[ offset ], address 表示首地址,offset表示相对于address的偏移
据此,需要求得各个"块"的首地址:
第一块(年份),因为是data segment的首位值,块首地址就是0H,本块共有21个元素,每个元素占 4 字节,那么一共占用 4 * 21 = 84字节,转换为十六进制就是 54H,因此本块占有的空间地址范围为 0 ~ 53H(这里地址是从0开始的,所以只能到53H) 第二块(公司总收入),同样的计算方式,块首地址是上一块的末端加一,53H + 1H = 54H, 本块也是21个元素,每个元素(double word)占用 4 字节,一共占用 4 * 21 = 84字节,54H + 54H - 1 = A7H;
第三块(公司雇员人数),同样得到块首地址: A7H + 1H = A8H
数据拷贝
明白了如何访问,那么接下来就要通过访问完成数据从data段到table段的拷贝,刚开始笔者没想到 es段寄存器,所以傻乎乎地使用了一个段寄存器ds来回切换… …这里介绍直接使用es的更简洁的编写方式。
首先,将两个数据段的首地址写入到 ds、es,以便完成后续地访问工作。 存储好了段位置,就可以显示地指定想要访问的数据段。 还需要做的一件事是:用寄存器保存各个段的偏移,说白了就是现在知道了数据的大块范围,但是需要的每一个具体的元素,因为8086CPU寄存器最多装入两个字节的数据,因此记录偏移位置是有必要的: 很凑巧的是,对于第一块的数据,每次需要操作 4 个字节,如 ‘1975’, 同样,对于第二块数据,double word 也是每次操作四个字节,所以可以只用一个寄存器表示偏移(每次更新偏移位置都是 + 4,往后偏四个字节即可),对于第三块数据,很遗憾,每次只需要偏移两个字节,与前两块的偏移"速度"不一致,必须额外使用一个寄存器去记录。
存放年份、公司总收入、公司人数
;存放年份
s: mov ax, [bx]
mov es:[di], ax
mov ax, [bx + 2]
mov es:[di + 2], ax
;存放公司总收入
mov ax, 54h[bx] ;第一个年收入的段基址 54H
mov dx, 56H[bx]
mov es:5h[di], ax
mov es:7h[di], dx
;存放公司人数
mov ax, 0A8H[si] ;第一个人数的段基址 A8
mov es:0AH[di], ax
存放公司总收入
这块需要使用到div指令,除数为word型时,需要同时使用 AX、DX来存储被除数,其中规则如下: 代码如下: 本次循环到此就结束了,为了循环正常进行,需要更新之前的偏移寄存器。
完整代码
assume cs:codesg
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'
;以上是表示21年份 0 ~ 83 -> 0 ~ 53H
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上表示21年公司总收入 84 ~ 167 -> 54 ~ 0A7H
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
;以上是表示21年公司雇员人数 164 ~ 205 -> A8 -> 0CDH
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
codesg segment
start:mov ax, data
mov ds, ax
mov ax, table
mov es, ax ;使用拓展段寄存器
mov bx, 0 ;data段偏移
mov si, 0 ;年份、收入的偏移
mov di, 0 ;雇员人数偏移
mov cx, 21 ;循环次数
;存放年份
s: mov ax, [bx]
mov es:[di], ax
mov ax, [bx + 2]
mov es:[di + 2], ax
;存放公司总收入
mov ax, 54h[bx] ;第一个年收入的段基址 54H
mov dx, 56H[bx]
mov es:5h[di], ax
mov es:7h[di], dx
;存放公司人数
mov ax, 0A8H[si] ;第一个人数的段基址 A8
mov es:0AH[di], ax
;计算人均收入并存放
mov ax, 54H[bx]
mov dx, 54H[bx + 2] ;初始化被除数 AX 存放低位
div word ptr ds:0A8H[si] ;除以人数
mov es:0dH[di], ax ;将商写入到table
;为下一次循环做准备
add bx, 4 ;年份 and 收入 偏移
add si, 2 ;雇员的偏移
add di, 16 ;di确定的是table的行基址
loop s
mov ax, 4c00h
int 21h
codesg ends
end start
|