1. 官方答案
assume cs:code
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 年的 21 个字符串
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514,
345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上是表示 21 年公司总收的 21 个 dword 型数据
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,45257,17800
;以上是表示 21 年公司雇员人数的 21 个 word 型数据
data ends
agency segment
db 8 dup(0)
agency ends
code segment
start: mov ax,0b800h
mov es,ax
mov di,0
mov cx,80*24
x: mov byte ptr es:[di],' ' ;将屏幕清空
mov byte ptr es:[di+1],0
inc di
inc di
loop x
mov ax,data
mov es,ax
mov di,0
mov bx,0
mov ax,agency
mov ds,ax
mov si,0
mov dh,4
mov cx,21
x1: push cx
mov ax,es:[di]
mov ds:[si],ax
mov ax,es:[di+2]
mov ds:[si+2],ax
mov byte ptr ds:[si+4],0 ;显示年份
mov dl,0
mov cl,2
call show_str
mov ax,es:[84+di]
push dx
mov dx,es:[84+di+2]
call dtoc_dword ;显示收入
pop dx
mov dl,20
mov cl,2
call show_str
mov ax,es:[84+84+bx]
call dtoc_word
mov dl,40 ;显示雇员数
mov cl,2
call show_str
mov ax,es:[84+di]
push dx
mov dx,es:[84+di+2]
div word ptr es:[84+84+bx] ;计算人均收入并显示
call dtoc_word
pop dx
mov dl,60
mov cl,2
call show_str
add di,4
add bx,2
add dh,1
pop cx
loop x1
mov ax,4c00h
int 21h
;名称:show_str
;功能:在屏幕的指定位置, 用指定颜色, 显示一个用 0 结尾的字符串
;参数:(dh)=行号, (dl)=列号(取值范围 0~80), (cl)=颜色, ds:si:该字符串的首地址
;返回:显示在屏幕上
show_str:
push ax
push cx
push dx
push es
push si
push di
mov ax,0b800h
mov es,ax
mov al,160
mul dh
add dl,dl
mov dh,0
add ax,dx
mov di,ax
mov ah,cl
show_str_x:
mov cl,ds:[si]
mov ch,0
jcxz show_str_f
mov al,cl
mov es:[di],ax
inc si
inc di
inc di
jmp show_str_x
show_str_f:
pop di
pop si
pop es
pop dx
pop cx
pop ax
ret
;名称:dtoc_word
;功能:将一个 word 型数转化为字符串
;参数:(ax)=word 型的数据, ds:si 指向字符串的首地址
;返回:ds:[si]放此字符串, 以 0 结尾
dtoc_word:
push ax
push bx
push cx
push dx
push si
mov bx,0
dtoc_word_x:
mov dx,0
mov cx,10
div cx
mov cx,ax
add dx,'0'
push dx
inc bx
jcxz dtoc_word_f
jmp dtoc_word_x
dtoc_word_f:
mov cx,bx
dtoc_word_x1:
pop ds:[si]
inc si
loop dtoc_word_x1
pop si
pop dx
pop cx
pop bx
pop ax
ret
;名称:dtoc_dword
;功能:将一个 double word 型数转化为字符串
;参数:(dx)=数的高八位, (ax)=数的低八位
;返回:ds:[si]放此字符串, 以 0 结尾
;备注:会用到 divdw 函数
dtoc_dword:
push ax
push bx
push cx
push dx
push si
mov bx,0
dtoc_dword_x:
mov cx,10
call divdw
push cx
inc bx
cmp ax,0
jne dtoc_dword_x
cmp dx,0
jne dtoc_dword_x
mov cx,bx
dtoc_dword_x1:
pop ds:[si]
add byte ptr ds:[si],'0'
inc si
loop dtoc_dword_x1
pop si
pop dx
pop cx
pop bx
pop ax
ret
;名称:divdw
;功能:除法, 被除数 32 位, 除数 16 位, 商 32 位, 余数 16 位, 不会溢出
;参数:(dx)=被除数高 16 位, (ax)=被除数低 16 位, (cx)=除数
;返回:(dx)=商高 16 位, (ax)=商低 16 位, (cx)=余数
divdw:
push bx
push ax
mov ax,dx
mov dx,0
div cx
mov bx,ax
pop ax
div cx
mov cx,dx
mov dx,bx
pop bx
ret
code ends
end start
2. 方法 2
; 将data段中的数据显示到屏幕上
assume cs:codesg,ds:datasg,ss:stacksg
; 数据段
datasg segment
; 表示公司21年的21个字符串, 经过数据分析, 偏移地址范围(0~53H)
db '1975', '1976', '1977', '1978', '1979', '1980', '1981', '1982', '1983'
db '1984', '1985', '1986', '1987', '1988', '1989', '1990', '1991', '1982'
db '1993', '1994', '1995'
; 表示21年公司总收入的21个dword型数据, 偏移地址范围(54H~0A7H)
dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065, 97479, 140417, 197514
dd 345980, 590827, 808530, 1183000, 1843000, 2759000, 3753000, 4649000, 5937000
; 表示21年公司雇员人数的21个word型数据, 偏移地址范围(0A8H, 0D1H)
dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1442, 2258, 2793, 4037, 5635, 8226
dw 11542, 14430, 15257, 17800
datasg ends
tablesg segment
; 定义的公司情况表格,一共定义21行;偏移地址范围(0~159H)
db 21 dup ('year summ ne ?? ')
tablesg ends
; 栈段
stacksg segment stack
db 128 dup(0)
stacksg ends
; 暂存段
tempsg segment
db 16 dup(0)
tempsg ends
; 代码段
codesg segment
start:
mov ax, datasg
mov ds, ax
mov ax, stacksg
mov ss, ax
mov sp, 128
; =====================================================================================
; 填充表格信息
mov ax, tablesg
mov es, ax
call fillTable
; =====================================================================================
; 清理屏幕
mov ax, 0B800H
mov es, ax
call clearScreen
; =====================================================================================
; 显示信息
call showYear
call showIncome
call showNumber
call showAverage
; =====================================================================================
mov ax, 4c00h
int 21h
; 功能:将data段中的数据按照如下格式写入到table段中, 并计算21年中的人均收入(取整)
fillTable:
mov si, 0
mov di, 0
mov cx, 21
fillTable1:
; 填写年份
mov ax, ds:[si]
mov es:[di], ax
mov ax, ds:[si+2]
mov es:[di+2], ax
mov byte ptr es:[di+4], 0H
; 填写公司总收入
mov ax, ds:[si+54h]
mov es:[di+5], ax
mov ax, ds:[si+56h]
mov es:[di+7], ax
mov byte ptr es:[di+9], 0H
add si, 4
add di, 16
loop fillTable1
mov si, 0
mov di, 0
mov cx, 21
fillTable2:
; 填写公司雇员人数
mov ax, ds:[si+0a8h]
mov es:[di+10], ax
mov byte ptr es:[di+12], 0H
; 计算人均收入
mov ax, es:[di+5]
mov dx, es:[di+7]
div word ptr es:[di+10]
mov es:[di+13], ax
mov byte ptr es:[di+15], 0H
add si, 2
add di, 16
loop fillTable2
ret
; 功能:清理屏幕 es指向显存
clearScreen:
mov bx, 0
mov dx, 0700H
mov cx, 2000
clearScreen1:
mov es:[bx], dx
add bx, 2
loop clearScreen1
ret
; 功能:显示年份
showYear:
mov ax, tablesg
mov ds, ax
mov cx, 21
mov dh, 4
mov dl, 0
mov si, 0
showYear1:
call showStr
inc dh
add si, 10H
loop showYear1
ret
; 功能:显示总收入
showIncome:
mov ax, tempsg
mov ds, ax
mov cx, 21
mov bx, 5
mov si, 0
mov dh, 4
mov dl, 7
showIncome1:
mov ax, tablesg
mov es, ax
push dx
mov ax, es:[bx]
mov dx, es:[bx+2]
call dtocdw
pop dx
call showStr
inc dh
add bx, 10H
loop showIncome1
ret
; 功能:显示人数
showNumber:
mov ax, tempsg
mov ds, ax
mov cx, 21
mov bx, 10
mov si, 0
mov dh, 4
mov dl, 16
showNumber1:
mov ax, tablesg
mov es, ax
push dx
mov ax, es:[bx]
call dtoc
pop dx
call showStr
inc dh
add bx, 10H
loop showNumber1
ret
; 功能:显示平均收入
showAverage:
mov ax, tempsg
mov ds, ax
mov cx, 21
mov bx, 13
mov si, 0
mov dh, 4
mov dl, 23
showAverage1:
mov ax, tablesg
mov es, ax
push dx
mov ax, es:[bx]
call dtoc
pop dx
call showStr
inc dh
add bx, 10H
loop showAverage1
ret
; 功能:在指定的位置, 显示一个用0结束的字符串
; @param dh存放行号, 取值范围(0~24)
; @param dl存放列号, 取值范围(0~79)
; @param ds:si存放指向字符串的首地址
showStr:
; 将显存段地址放入es拓展寄存器中
mov ax, 0B800H
mov es, ax
push si
push dx
push cx
; 计算出行的偏移地址
xor ax, ax
mov al, 160
mul dh
push ax ; 寄存器不够用, 先放入栈中
; 计算出列的偏移地址
xor ax, ax
mov al, 2
mul dl
; 将行列偏移地址相加, 得到具体位置的偏移地址,放在di中
pop dx
add dx, ax
mov di, dx
; 拿取字符串, 放入显存
mov ah, 10 ; 将字体属性放到ah中
putStr:
xor cx, cx
mov cl, ds:[si]
jcxz endPutStr
mov al, ds:[si] ; 将字符串的ascll码放到al中, 这样ax就是一个有属性的可显示字符了
mov es:[di], ax
inc si ; 字符串地址+1, 指向下一个字符
add di, 2 ; 显示具体位置偏移地址+2, 指向下一个可显示字符
jmp short putStr
endPutStr:
pop cx
pop dx
pop si
ret
; 功能:进行不会产生溢出的除法运算
; @param ax 存放被除数的低16位
; @param dx 存放被除数的高16位
; @param cx 存放除数
; @return ax 商的低16位
; @return dx 商的高16位
; @return cx 余数
divdw:
push bx
push si ; 保留使用的寄存器
mov bx, ax ; 保存低位
mov ax, dx
mov dx, 0H ; 进行H/N 的运算
div cx ; 因为除数为16位的, 使用div, 则设置的被除数为32位
mov si, ax ; 用于保存int(H/N), 因为rem(H/N)*65536就相当于高16位, dx了, 故得到的余数在dx直接做高位
mov ax, bx ; [rem(H/N)*65536+L]
div cx ; [rem(H/N)*65536+L]/N, 结果是的ax保存为16位的低位
mov cx, dx ; 将余数写入cx
mov dx, si ; 将高位写入dx
pop si
pop bx ; 还原寄存器的值
ret
; 功能:将word型数据转变为表示十进制的字符串, 字符串以0结尾
; @param ax 要转换的word型数据
; @param ds:si 存放字符串存储的首地址
dtoc:
push cx
push bx
; 将十六进制数值转化为十进制ascll码
mov di, si
xor bx, bx
push bx ; 设置字符串的结束标志
mov bx, 10
dtoc1:
xor dx, dx
div bx ; ÷10
mov cx, ax ; 将商给cx
add dx, 30H ; 0~9数字的ascll码 = 自己 + 30H
push dx ; 将转换好的ascll码入栈
jcxz dtoc2
jmp dtoc1
dtoc2:
pop cx ; 获取入栈的字符串
jcxz dtoc3
; 到了这里, 栈里已经存放了转换好的字符串, 我们拿出来放到ds:si的备份ds:di中即可
mov byte ptr ds:[di], cl
inc di
jmp dtoc2
dtoc3:
mov byte ptr ds:[di], 0
pop bx
pop cx
ret
; 功能:将dword型数据转变为表示十进制的字符串, 字符串以0结尾
; @param ax 要转换的dword型数据低16位 dx 要转换的dword型数据高16位
; @param ds:si 存放字符串存储的首地址
dtocdw:
push cx
; 将十六进制数值转化为十进制ascll码
mov di, si
xor cx, cx
push cx ; 设置字符串的结束标志
dtocdw2:
mov cx, 10
call divdw ; ÷10
add cx, 30H ; 0~9数字的ascll码 = 自己 + 30H
push cx ; 将转换好的ascll码入栈
xor cx, cx
add cx, ax
add cx, dx
jcxz dtocdw1
jmp dtocdw2
dtocdw1:
pop cx ; 获取入栈的字符串
jcxz dtocdw3
; 到了这里, 栈里已经存放了转换好的字符串, 我们拿出来放到ds:si的备份ds:di中即可
mov byte ptr ds:[di], cl
inc di
jmp dtocdw1
dtocdw3:
mov byte ptr ds:[di], 0
pop cx
ret
codesg ends
end start
3. 课件版
assume cs:code,ds:data,ss:stack
data segment
; 表示21年的21个字符串 年份的数据占了21*4=84=54H
; 1975 = 31 39 37 35 = 4byte = FF = 2^8 = 0-255
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个dd型数据 收入的数据占了21*4=84=54H 4字节 32bit
; 16 = 10 00 00 00 = 4byte = 2^32 = 0-4,294,967,295
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
; 表示21年公司雇员人数的21个word型数据 雇员的数据占了21*2=42=2AH 2字节 16bit
; 3 = 03 00 = 2byte = 2^16 = 0-65535
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
; 年份是0H~53H, 收入是54H~A7H, 雇员是A8H~D1H
; data段地址+84(年份所占字节)=收入的数据起始地址
; data段地址+168(年份所占字节84+收入所占字节)=雇员的数据起始地址
; -d ds:0
; 0E24:0000 31 39 37 35 31 39 37 36-31 39 37 37 31 39 37 38 197519761977197
; 0E24:0010 31 39 37 39 31 39 38 30-31 39 38 31 31 39 38 32 197919801981198
; 0E24:0020 31 39 38 33 31 39 38 34-31 39 38 35 31 39 38 36 198319841985198
; 0E24:0030 31 39 38 37 31 39 38 38-31 39 38 39 31 39 39 30 198719881989199
; 0E24:0040 31 39 39 31 31 39 39 32-31 39 39 33 31 39 39 34 199119921993199
; 0E24:0050 31 39 39 35 10 00 00 00-16 00 00 00 7E 01 00 00 1995........~..
; 0E24:0060 4C 05 00 00 56 09 00 00-40 1F 00 00 80 3E 00 00 L...V...@....>.
; 0E24:0070 A6 5F 00 00 91 C3 00 00-C7 7C 01 00 81 24 02 00 ._.......|...$.
; 0E24:0080 8A 03 03 00 7C 47 05 00-EB 03 09 00 CA 42 0C 00 ....|G.......B.
; 0E24:0090 18 0D 12 00 38 1F 1C 00-58 19 2A 00 28 44 39 00 ....8...X.*.(D9
; 0E24:00A0 28 F0 46 00 68 97 5A 00-03 00 07 00 09 00 0D 00 (.F.h.Z........
; 0E24:00B0 1C 00 26 00 82 00 DC 00-DC 01 0A 03 E9 03 A2 05 ..&............
; 0E24:00C0 D2 08 E9 0A C5 0F 03 16-22 20 16 2D 5E 38 99 3B ........" .-^8.
; 0E24:00D0 88 45 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .E.............
; 0E24:00E0 79 65 61 72 20 73 75 6D-6D 20 6E 65 20 3F 3F 20 year summ ne ??
; 0E24:00F0 79 65 61 72 20 73 75 6D-6D 20 6E 65 20 3F 3F 20 year summ ne ??
data ends
table segment
; 0123456789ABCDEF
db 21 dup ('year summ ne ?? ')
table ends
stack segment stack
db 128 dup (0)
stack ends
string segment
db 10 dup ('0'),0,0,0
string ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,128
call input_table
call clear_screen
call output_table
mov ax,4C00H
int 21H
;=========================================================
clear_screen:
mov bx,0B800H
mov es,bx
mov bx,0
mov dx,0700H
mov cx,2000
clearScreen: mov es:[bx],dx
add bx,2
loop clearScreen
ret
;=========================================================
output_table: call init_reg_outputTable
mov cx,21
mov si,0 ;ds:[si]
mov di,160*3
mov bx,9 ;string[9]
outputTable: call show_year
call show_sum
call show_employee
call show_average
add di,160
add si,16
loop outputTable
ret
;=========================================================
show_average: push ax
push bx
push cx
push dx
push ds
push es
push si
push di
mov ax,ds:[si+13]
mov dx,0
call isShortDiv
call init_reg_showString
add di,40*2
; 0123456789ABCDEF
; db 21 dup ('year summ ne ?? ')
call show_string
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;=========================================================
show_employee: push ax
push bx
push cx
push dx
push ds
push es
push si
push di
mov ax,ds:[si+10]
mov dx,0
call isShortDiv
call init_reg_showString
add di,23*2
; 0123456789ABCDEF
; db 21 dup ('year summ ne ?? ')
call show_string
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;=========================================================
show_sum: push ax
push bx
push cx
push dx
push ds
push es
push si
push di
mov ax,ds:[si+5]
mov dx,ds:[si+7]
call isShortDiv
call init_reg_showString
add di,10*2
; 0123456789ABCDEF
; db 21 dup ('year summ ne ?? ')
call show_string
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;=========================================================
show_string: push cx
push ds
push es
push si
push di
showString: mov cx,0
mov cl,ds:[si]
jcxz showStringRet
mov es:[di],cl
add di,2
inc si
jmp showString
showStringRet: pop di
pop si
pop es
pop ds
pop cx
ret
;=========================================================
init_reg_showString:
mov si,bx
mov bx,string
mov ds,bx
mov bx,0B800H
mov es,bx
ret
;=========================================================
isShortDiv: mov cx,dx
jcxz shortDiv
push ax
mov bp,sp
mov cx,10
call long_div ; ip ax
add sp,2
add cl,30H
mov es:[bx],cl
dec bx
jmp isShortDiv
shortDivRet: ret
;=========================================================
long_div:
mov ax,dx
mov dx,0
div cx
push ax
mov ax,ss:[bp+0]
div cx
mov cx,dx
pop dx
ret
;=========================================================
shortDiv: mov cx,10
div cx
add dx,30H
mov es:[bx],dl
mov cx,ax
jcxz shortDivRet
dec bx
mov dx,0
jmp shortDiv
;=========================================================
show_year: push bx
push cx
push ds
push es
push si
push di
mov bx,0B800H
mov es,bx
mov cx,4
add di,3*2
showYear: mov dl,ds:[si]
mov es:[di],dl
inc si
add di,2
loop showYear
pop di
pop si
pop es
pop ds
pop cx
pop bx
ret
;=========================================================
init_reg_outputTable:
mov bx,table
mov ds,bx
mov bx,string
mov es,bx
ret
;=========================================================
input_table: call init_reg_intputTable
mov si,0
mov di,21*4*2 ;ds:[si] ds:[di] es:[bx]
mov bx,0
mov cx,21
inputTable: push ds:[si+0]
pop es:[bx+0]
push ds:[si+2]
pop es:[bx+2]
mov ax,ds:[si+21*4]
mov dx,ds:[si+21*4+2]
mov es:[bx+5],ax
mov es:[bx+7],dx
push ds:[di+0]
pop es:[bx+10]
div word ptr es:[bx+10]
mov es:[bx+13],ax
; 0123456789ABCDEF
; db 21 dup ('year summ ne ?? ')
add si,4
add di,2
add bx,16
loop inputTable
ret
;=========================================================
init_reg_intputTable:
mov bx,data
mov ds,bx
mov bx,table
mov es,bx
ret
code ends
end start