;===============================================================================
;课程设计1
;将实验7中的power idea公司的数据按照格式在屏幕上显示.
;===============================================================================
ASSUME CS:CODE,DS:DATA
;-------------------------------------------------------------------------------
DATA SEGMENT
;年份
YEAR DB '1975','1976','1977','1978','1979','1980','1981','1982','1983'
DB '1984','1985','1986','1987','1988','1989','1990','1991','1992'
DB '1993','1994','1995'
;年收入
INCOME DD 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
DD 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;雇员人数
EMPLOYEE_NUM DW 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
DW 11542,14430,15257,17800
AVERAGE DW 21 DUP (0)
TABLE DB 21 DUP ('YEAR ',0)
;字符串的长度为25H(包括结尾符)
DATA ENDS
;--------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
CODE SEGMENT
START:
MOV AX,DATA ;初始化DS
MOV DS,AX
;输出年份的字符串到数据表各年字符串的年份段
MOV CX,21 ;循环次数=21,有21组数据
MOV BX,0 ;年份的起始偏移地址
MOV SI,OFFSET TABLE ;输出数据的表的起始偏移地址
STORE_ALL_YEAR: ;保存所有的年份
PUSH CX ;压入外层循环次数
MOV CX,4 ;年份有4个字符,循环次数为4
STORE_ONE_YEAR: ;保存一年的年份
MOV AL,[BX] ;字符转移到AL中
MOV [SI],AL ;AL中的字符转移到表
INC BX ;BX+1,准备读取下一个字符
INC SI ;SI+1,准备写入下一个字符
LOOP STORE_ONE_YEAR ;写入下一个字符
ADD SI,21H ;定位到下一年份的起始偏移地址
POP CX ;恢复外层循环次数
LOOP STORE_ALL_YEAR ;准备写入下一年份的数据
;转换收入为字符串并输出到数据表各年字符串的收入段
MOV CX,21 ;循环次数=21,有21组数据
MOV SI,OFFSET TABLE ;输出数据的表的起始偏移地址
ADD SI,10 ;指向第一组数据的收入那一项的偏移地址
STORE_ALL_INCOME: ;储存所有年份的收入
PUSH SI ;保存当前字符串收入项内存单元的偏移地址
MOV AX,[BX] ;低16位
ADD BX,2 ;高16位
MOV DX,[BX]
CALL DTOC ;转化为字符串
CALL NEXT_STRING ;SI指向下一个字符串收入项内存单元的偏移地址
DEC SI ;SI指向收入项字符串的结尾符0
MOV BYTE PTR [SI],' ' ;替换为空格
POP SI ;恢复为当前字符串收入项内存单元的偏移地址
ADD BX,2 ;准备读取下一年份的收入
ADD SI,25H ;SI指向数据表中下一年份收入那一项的偏移地址
LOOP STORE_ALL_INCOME ;储存下一年份的收入数据
;转换雇员数为字符串并输出到数据表各年字符串的雇员数段
MOV CX,21 ;循环次数=21,有21组数据
MOV SI,OFFSET TABLE ;输出数据的表的起始偏移地址
ADD SI,20 ;指向第一组数据的雇员数那一项的偏移地址
STORE_ALL_EMPLOYEE_NUM: ;储存所有年份的雇员数
PUSH SI ;保存当前字符串雇员数项内存单元的偏移地址
MOV AX,[BX] ;低16位,(AX)=雇员数
MOV DX,0 ;高16位置零
CALL DTOC ;转化为字符串
CALL NEXT_STRING ;SI指向下一个字符串雇员数项内存单元的偏移地址
DEC SI ;SI指向雇员数项字符串的结尾符0
MOV BYTE PTR [SI],' ' ;替换为空格
POP SI ;恢复当前字符串雇员数项内存单元的偏移地址
ADD BX,2 ;准备读取下一年份的雇员数
ADD SI,25H ;SI指向数据表中下一年份雇员数那一项的偏移地址
LOOP STORE_ALL_EMPLOYEE_NUM ;存储下一年份的雇员数
;计算各年的人均收入并储存到AVERAGE项
MOV CX,21 ;循环次数=21,有21组数据
MOV SI,OFFSET AVERAGE ;(SI)=AVERAGE项的起始偏移地址
MOV BX,OFFSET INCOME ;(BX)=INCOME项的起始偏移地址
MOV DI,OFFSET EMPLOYEE_NUM ;(DI)=EMPLOYEE_NUM项的起始偏移地址
GET_AVERAGE:
PUSH CX ;保存外层循环次数
MOV AX,[BX] ;(AX)=收入的低16位
ADD BX,2 ;BX+2,准备读取收入的高16位
MOV DX,[BX] ;(DX)=收入的高16位
ADD BX,2 ;BX+2,准备读取下一年份的收入
MOV CX,[DI] ;(CX)=雇员数
ADD DI,2 ;DI+2,准备读取下一年份的雇员数
DIV CX ;(AX)=商=人均收入AVERAGE
MOV [SI],AX ;转移到AVERAGE的内存单元中
ADD SI,2 ;SI+2,准备写入下一年份的人均收入
POP CX ;恢复外层循环次数
LOOP GET_AVERAGE ;存储下一年份的平均收入AVERAGE
;转换人均收入为字符串并输出到数据表各年字符串的人均收入段
MOV CX,21 ;循环次数=21,有21组数据
MOV BX,OFFSET AVERAGE ;(BX)=AVERAGE项的起始偏移地址
MOV SI,OFFSET TABLE ;输出数据的表的起始偏移地址
ADD SI,29 ;定位到AVERAGE项
STORE_ALL_EVERAGE: ;储存所有年份的平均收入
PUSH SI ;保存当前字符串人均收入项内存单元的偏移地址
MOV AX,[BX] ;(AX)=人均收入的低16位
MOV DX,0 ;(DX)=人均收入的高16位(为0)
CALL DTOC ;转化为字符串
CALL NEXT_STRING ;SI指向下一个字符串人均收入项内存单元的偏移地址
DEC SI ;SI指向人均收入项字符串的结尾符0
MOV BYTE PTR [SI],' ' ;替换为空格
POP SI ;恢复当前字符串人均收入项内存单元的偏移地址
ADD BX,2 ;准备读取下一年份的人均收入
ADD SI,25H ;SI指向数据表中下一年份人均收入那一项的偏移地址
LOOP STORE_ALL_EVERAGE ;存储下一年份的人均收入
;输出各年的字符串
MOV CX,21 ;循环次数=21,有21组数据
MOV SI,OFFSET TABLE ;输出数据的表的起始偏移地址
MOV DH,4 ;起始行数=4
MOV DL,1 ;起始列数=1
PRINT: ;向屏幕输出
PUSH CX ;保存外层循环次数
MOV CL,7 ;颜色=白色
CALL SHOW_STR ;显示一年的字符串
POP CX ;恢复外层循环次数
ADD SI,25H ;SI指向下一个字符串开始的内存单元的偏移地址
INC DH ;行号+1
LOOP PRINT ;输出下一年份的字符串
MOV AX,4C00H ;程序返回
INT 21H
;----------------------------------------------------------------------------
;名称:DTOC(DIGITAL TOCHARACTER)
;功能:将DWORD型数据转变为表示十进制数的字符串,字符串以0为结尾符。
;参数:(AX)=DWORD型数据的低16位,(DX)=DWORD型数据的高16位,(SI),DS:SI指向字符串的首地址
;返回:无
DTOC PROC NEAR
PUSH BX ;保护现场
PUSH DX
PUSH CX
PUSH AX
PUSH SI
MOV BX,0 ;BX用来计数,转化了多少位数字
DECIMAL_PLACE:
MOV CX,10 ;除数=10
CALL DIVDW ;(AX)=商的低16位,(DX)=商的高16位,(CX)=余数
ADD CX,30H ;数字转化为ASCII码
PUSH CX ;压入(CL)=每一位数字的ASCII码
INC BX ;转化的位数+1
CMP DX,0 ;商的高16位是不是0?不是则未取得10进制数的每一位数字
JNE DECIMAL_PLACE ;如果不是0,则还没有转化完,继续取得下一位数字
CMP AX,0 ;商的低16位是不是0?不是则未取得10进制数的每一位数字
JNE DECIMAL_PLACE ;如果不是0,则还没有转化完,继续取得下一位数字
TRANSFER: ;依次把十进制数每一位数的ASCII码送到DS:SI开始的内存单元
POP CX ;弹出一位数字的ASCII码
MOV [SI],CL ;传送到DS:[SI]处
INC SI ;SI+1,准备传送下一个字符的ASCII码
DEC BX ;每传送一个字符循环次数-1
CMP BX,0 ;循环终止条件
JNE TRANSFER ;如果不为零继续传送
MOV AL,0
MOV [SI],AL ;改写数字字符串的最后一个byte的数据为0作为结尾符
POP SI ;恢复现场
POP AX
POP CX
POP DX
POP BX
RET ;返回
DTOC ENDP
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------
;名称:DIVDW
;功能:进行不会产生溢出的除法运算,被除数位dword型,除数为word型,
; 结果为dword型。
;参数:(AX)=dword型数据的低16位 ,(DX)=dword型数据的高16位 ,(CX)=除数
;返回:(AX)=结果的低16位 ,(DX)=结果的高16位 ,(CX)=余数
;应用的公式:X/N = INT(H/N)*FFFF + [REM(H/N)*FFFF+L]/N
;X=被除数(0~FFFFFFFF),N=除数(0~FFFF),H=X的高16位(0~FFFF),
;L=X的低16位(0~FFFF),INT():取商,REM():取余
DIVDW PROC NEAR
PUSH BX
PUSH CX
PUSH AX
MOV AX,DX ;把(DX)储存到AX中
MOV DX,0 ;(DX)置零
DIV CX ;16位除法,H/N,(AX)=商,(DX)=余数
MOV BX,AX ;把AX中高16位的结果转移到BX中
MOV AX,0 ;(DX)=REM(H/N)*FFFF高16位,(AX)=REM(H/N)*FFFF低16位=0
MOV CX,AX ;(CX)=REM(H/N)*FFFF低16位
POP AX ;(AX)=X的低16位
ADD CX,AX ;(CX)=REM(H/N)*FFFF+L的低16位
MOV AX,CX ;(AX)=REM(H/N)*FFFF+L的低16位
POP CX ;恢复除数N
DIV CX ;(AX)=结果的低16位,(DX)=结果的余数
MOV CX,DX ;(CX)=结果的余数
MOV DX,BX ;(DX)=结果的高16位
POP BX
RET
DIVDW ENDP
;-------------------------------------------------------------------
;-----------------------------------------------------------------------------
;功能:将SI从本字符串开始的内存单元的偏移地址指向下一个字符串开始的内存单元的
; 偏移地址,字符串以0为结尾符。
;参数:(SI)=本字符串开始的内存单元的偏移地址
;返回:(SI)=下一个字符串开始的内存单元的偏移地址
NEXT_STRING PROC NEAR
PUSH AX ;保护现场
ZERO_OR_NOT:
MOV AX,[SI]
MOV AH,0 ;!!!如果AX的低8位(要比较的字符)为0而高8位不为0
;!!!则程序不能如期望的那样跳转到END_OF_NUM_STRING
CMP AX,0 ;是不是结尾符0?
JE END_OF_NUM_STRING ;是,则跳转到END_OF_NUM_STRING
INC SI ;否,SI+1
JMP ZERO_OR_NOT ;检测下一个byte
END_OF_NUM_STRING: ;是结尾符0
INC SI ;SI+1,指向下一个字符串开始的内存单元的偏移地址
POP AX ;恢复现场
RET ;返回
NEXT_STRING ENDP
;------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
;功能:在指定的位置,用指定的颜色,显示一个用0结尾的字符串。
;参数:(DH)=行号(0~24),(DL)=列号(0~79),(CL)=颜色,DS:SI指向字符串的首地址。
;返回:无
SHOW_STR PROC NEAR
PUSH AX ;保护现场
PUSH ES
PUSH DX
PUSH DI
PUSH SI
MOV AX,0B800H ;显示缓冲区段地址
MOV ES,AX ;(ES)=显示缓冲区段地址
MOV AL,0A0H ;以下计算初始字符的偏移地址
MUL DH ;行数×0A0H(160个字节)
MOV DI,AX ;转移到DI中
MOV AL,2 ;显示缓冲区中一个字符占两个字节空间
MUL DL ;2×列号
ADD DI,AX ;获得初始字符的偏移地址
S:
MOV AX,DS:[SI] ;输出字符到显示缓冲区
MOV ES:[DI],AX
INC DI ;准备写入颜色信息
MOV ES:[DI],CL ;写入颜色信息
INC SI ;准备输出下一个字符
PUSH CX ;保存颜色=(CL)
MOV CX,DS:[SI] ;(CX)=下一个字符
MOV CH,0 ;!!!若DS:[SI]的低位字节为零,但其高位字节不为零,
;!!!则程序不能如期望的那样跳转到END_SHOW
JCXZ END_SHOW ;不为零则继续输出,为零则结束子程序
POP CX ;恢复颜色=(CL)
INC DI ;准备写入下一个字符
JMP S ;输出下一个字符
END_SHOW:
POP CX ;!!!如果(CX)≠0,就会跳转到这里,此时(CX)在栈中还没有弹出
;!!!如果不弹出就会引发错误
POP SI ;恢复现场
POP DI
POP DX
POP ES
POP AX
RET
SHOW_STR ENDP
;--------------------------------------------------------------------------------
CODE ENDS
END START
;--------------------------------------------------------------------------------
1966

被折叠的 条评论
为什么被折叠?



