assume cs:code, ds:data;, ds:table, ss:stack
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,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
stack segment
db 512 dup (0)
stack ends
code segment
divdw:
push ax
mov ax, dx
mov dx, 0
div cx
mov bx, ax
pop ax
div cx
mov cx, dx
mov dx, bx
ret
average_str: ;该段输出人均收入,收入/雇员=人均
push ax
push bp
push dx
push es
push cx
push di
average_one:
push cx
mov bx, 0
mov ax, word ptr ds:[si + 84] ;因有被除数大于65536所以调用两个寄存器ax储存低位
add si, 2
mov dx, word ptr ds:[si + 84] ;被除数高位传给dx寄存器
add si, 2
div word ptr ds:[di + 168] ;int(16位位移/(H * 65536 + L))
add di, 2 ;
push di ;因为下面的程序还需要使用di所以暂时把di中的数据放入栈中当此段程序结束开始下一个循环前在用pop调出付给di
mov di, 0 ;因为已经把di中的数据放入栈中所以现在可以初始化di中的数据为零让下面的程序可以使用di寄存器
average_two: ;下面的程序就跟employee_str中的程序相似,上面的数据相当于em中的mov ax, word ptr ds:[si]
mov cx, 10
mov dx, 0
div cx
add dx, '0'
push dx
inc bx
mov cx, ax
jcxz average_three
jmp short average_two
average_three:
mov cx, bx
average_four:
pop ax
mov ah, 2
mov byte ptr es:[bp + di], al
mov byte ptr es:[bp + di + 1], ah
add di, 2
loop average_four
pop di ;恢复di中的数据,已备循环使用div word ptr ds:[di + 168]
pop cx
add bp, 160
loop average_one
pop di
pop cx
pop es
pop dx
pop bp
pop ax
ret
employee_str: ;因为该段跟下面的运行方式差不多再次就不做标注了该段是输出雇员的数据
push ax
push bp
push dx
push es
push cx
push di
employee_one:
push cx
mov bx, 0
mov ax, word ptr ds:[si]
add si, 2
employee_two:
mov cx, 10
mov dx, 0
div cx
add dx, '0'
push dx
inc bx
mov cx, ax
jcxz employee_three
jmp short employee_two
employee_three:
mov cx, bx
employee_four:
pop ax
mov ah, 2
mov byte ptr es:[bp + di], al
mov byte ptr es:[bp + di + 1], ah
add di, 2
loop employee_four
pop cx
mov di, 0
add bp, 160
loop employee_one
pop di
pop cx
pop es
pop dx
pop bp
pop ax
ret
income_str:
push ax
push bp
push dx
push es
push cx
push di
income_one:
push cx ;把cx压入栈中,因为cx还需要被调用,压入栈中后就可以暂时保存起来后期再调用
mov bx, 0 ;进行第二次循环的时候bx中带有数值,所以这里要进行归零设置
mov ax, word ptr ds:[si]
add si, 2
mov dx, word ptr ds:[si]
add si, 2
income_two:
push bx ;下面的计算中我们需要用到bx寄存器所以先把bx中的数据压入栈中暂存
mov cx, 10 ;除数
call divdw ;无溢出除法运算
pop bx ;bx调出里面的数据是inc bx自加后的数
add cx, '0'
push cx ;把无溢出除法运算中得到的余数推入栈中
inc bx ;因为下面我们还需要从栈中取出上面计算的余数这个是用来计数的记录我们一共向栈中压入了多少余数
mov cx, ax ;divdw中的商传给cx已被jcxz income_three验证使用
jcxz income_three ;如验证cx中的数值为0就跳转到income_two标识处向下执行
jmp short income_two ;无条件跳转到标识处起到循环的作用
income_three:
mov cx, bx ;把上面计数的bx中的数值传给cx供loop income_four使用
income_four:
pop ax ;取出栈中的余数
mov ah, 2 ;显示属性
mov byte ptr es:[bp + di], al
mov byte ptr es:[bp + di + 1], ah
add di, 2
loop income_four ;此处为内循环,输出每一行相应的数字
pop cx
mov di, 0
add bp, 160
loop income_one ;行数循环,控制参数在上面pop cx这个
pop di ;从栈中取回寄存器中的初始化数据
pop cx
pop es
pop dx
pop bp
pop ax
ret
year_str:
push ax
push bp
push dx
push es
push cx
push di ;因为下一个call还需要使用以上寄存器所以先把这些寄存器的初始值保存到栈中,在该段程序执行完毕后再取出再对应的寄存器中
year_one:
push cx ;在year_str这段程序又再次使用了push cx因为loop year_one循环
mov cx, 4 ;该cx给year_two使用
year_two:
mov al, byte ptr ds:[si] ;内存地址储存的数据需要一个通用寄存器才能储存在其他的内存地址中所以这里面借用了一个al寄存器
mov byte ptr es:[bp + di], al ;
mov byte ptr es:[bp + di + 1], ah
inc si ;因为需要循环拾取数据所以si自加
add di, 2
loop year_two ;此处为内循环,一共申诉出21行4列的字符这个循环控制每行中的列数
pop cx ;取出给loop year_one使用
mov di, 0 ;bp加了160他就可以去下一行输出,所以di从零开始
add bp, 160 ;bp加160
loop year_one ;行循环
pop di ;取回,本次结束
pop cx
pop es
pop dx
pop bp
pop ax
ret
start:
mov ax, data
mov ds, ax
mov ax, 0b800h
mov es, ax
mov ax, stack
mov ss, ax
mov sp, 512
mov bp, 480 ;从什么地方开始
mov cx, 21 ;输出多少行
mov ah, 2 ;输出属性
mov di, 0
mov si, 0
call year_str ;年月输出
mov bp, 500 ;从新定义bp的数值已被es:使用
call income_str ;从从该处开始输出收入
mov bp, 520 ;从新定义bp的数值已被es:使用
call employee_str
mov bp, 540
mov si, 0
call average_str
mov ax, 4c00h
int 21h
code ends
end start
下面为运行的结果