- [springaccount] O(∩_∩)O哈哈~ 谢谢老师的精彩点评 我又有激情了…… 01/06 17:10
- [游客] 谢谢你啊 呵呵 12/23 11:15
- [游客] 252025628 如果我说的不对,希望博主联系我,还请赐教 09/17 05:51
- [游客] s:mov [bx],bx 也不对吧 应该是 mov [bx],bl 因为内存单元是字节数据,而b 09/17 05:49
- [游客] 能问个问题吗? 我没不明白为什么我用这种方法写把内容写入显存 但是运行什么也不显示 不知 06/11 22:37
- [rswjf] 你的子程序描述的太好了,向你学习!!! 06/03 23:10
- [rswjf] 刚刚做完这个课程,真花时间。一看就知道你做得过程很清楚。我的。。。。。明天再改了。 06/03 22:57
- [游客] 第5题要把mov bx,0放在循环外吧 05/23 13:33
- [游客] 很好很强大 值得学习 顶了 04/20 18:13
- [jiajiade01] 谢谢老师的鼓励.您们才是让我们这些学习者值得尊敬的,在百忙中抽出时间认真的批阅这些枯燥的数据是很不容 03/24 22:07
[2009-03-24 11:56] 王爽 《汇编语言》 课程设计一
任务:将实验7中的POWer idea公司的数据按照上图的格式在屏幕上显示出来.
######先完成适合word型或dword型数据的数值显示子程序dtoc######
assume cs:code
data segment
dd 5937000
data ends
zczfc segment
db 11 dup(0) ;暂存dword型数据处理后的字符串.dword型数据转换成十进制后的字符串的个数和字符串结束标志0最大占有11个字节.
zczfc ends
code segment
start:
mov ax,zczfc
mov ds,ax
mov si,0 ;ds:si指向保存字符串的段的首地址.
mov ax,data ;读取数据并向指定的寄存器赋值.
mov es,ax
mov di,0
mov ax,es:[di] ;dword型数据的低位.
mov dx,es:[di].2 ;dword型数据的高位(当数据为word型,此处为mov dx,0).
call dtoc
mov dh,8 ;显示字符串
mov dl,3
mov cl,2 ;字符属性.
call show_str
mov ax,4c00h
int 21h
;名称:dtoc(数值转字符串)
;功能:将dword型或word型数转变为表示十进制数的字符串,字符串以0为结尾符.
;参数:(ax)=dword型数据的低16位.
; (dx)=dword型数据的高16位.
; ds:si指向字符串首地址.
;返回:无
;这个子程序不能称为数值显示,它实现的功能只是对数值进行字符串的转变,并为字符串显示子程序做了一些必要的处理.当然这两个子程序
;可以融为一体,我做过试验,可以实现,但是操作比较复杂,特别是对栈的操作,虽然比较费脑筋,但是我对于栈的实质有了更加深入的了解.
dtoc:
push cx
push ax
push dx
push si
mov cx,0
push cx ;作为字符串处理结束标记.
dc1:mov cx,10 ;除数(为取得数据的十进制各数位做准备).
call divdw ;取余数(循环取得各数位,从低位到高位).
add cx,30H ;数位转换为ASCII码.
push cx ;暂存数位的ASCII码.
mov cx,dx ;判断商是否为零来确定数据字符串处理结束,必须满足dx和ax同时为零,以下作此处理.
jcxz dc2
jmp short dc1
dc2:mov cx,ax
jcxz clzc
jmp short dc1
clzc:pop cx ;栈顶是数据的最高位的ASCII码,当数值以字符串的形式在内存中存放时是顺序存放的,就是说数值的最高位的ASCII码
;存放在存储这个数值字符串的内存段中的最低位,依此类推.
mov [si],cl ;在指定的zczfc的段中依次保存字符串,因为余数小于0ah,加上30H后不会大于FFH,所以数位的ASCII码一定是只在cl中
mov ch,0 ;ch和cl结合来判定cx是否为零.
jcxz okdc ;当遇到前面最先入栈的0时字符串处理结束.
inc si ;指向下一个保存位置
jmp short clzc ;处理下一个数位ASCII码.
okdc:pop si ;作为字符串结束的标记0已经写在字符串的尾部.
pop dx
pop ax
pop cx
ret
divdw:
push bx
push ax ;暂存X的低16位L.
push dx ;暂存X的高16位H.
;处理int(H/N)*65536
mov dx,0 ;置H为dword型,(dx)是H的高16位
pop ax ;dx出栈送入ax中作为H的低16位
div cx ;H/N
mov bx,ax ;(ax)是int(H/N)作为返回值的高16位暂存在bx中
;处理[rem(H/N)*65536+L]/N.设rem(H/N)*65536+L=a,rem(H/N)作为dword型数据a的高16位放在dx中,L作为低16位放在ax中.
pop ax ;(dx)=rem(H/N),(ax)=L
div cx ;[rem(H/N)*65536+L]/N
mov cx,dx ;(dx)作为返回值的余数送入cx中
mov dx,bx ;返回值的高16位.(ax中存放的是返回值的低16位)
pop bx
ret
show_str:
push es
push ax
push dx
push di
push cx
push bx
push si
mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov di,ax ;确定行
add dl,dl ;列乘2是字符
mov dh,0
mov bx,dx
mov al,cl
s:mov ah,[si] ;以下写入显存
mov es:[di][bx],ah
mov es:[di][bx+1],al
mov ch,0
mov cl,[si]
jcxz ok
add bx,2
inc si
jmp s
ok:
pop si
pop bx
pop cx
pop di
pop dx
pop ax
pop es
ret
code ends
end start
##########课程设计一完整代码##########
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'
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
zczfc segment
db 11 dup(0) ;暂存dword型数据处理后的字符串.dword型数据转换成十进制后的字符串的个数加上字符串结束标志0最大占有11个字节.
zczfc ends
code segment
start:
mov ax,data
mov es,ax
sub di,di ;es:di先指向第一个准备载入的数据在内存中的首地址.(没有用 mov di,0 ,呵呵,为内存节省一个字节)
mov ax,zczfc
mov ds,ax
sub si,si ;ds:si指向保存字符串所在段的首地址.
;;;;;;;;;;;;;显示年份;;;;;;;;;;;;;;
mov cx,21 ;循环处理并显示21个年份.
mov dh,1
nfz1:push cx ;因为下面要用到cx寄存器.
mov cx,4 ;因为年份是4位.
nfz2:mov al,es:[di] ;把年份写入zczfc段中,为年份的显示进行必要的处理.
mov [si],al
inc si
inc di ;es:di指向下一个内存单元(data段).
loop nfz2
mov byte ptr [si],0 ;字符串结束标志.
sub si,si ;si清零,ds:si指向字符串首地址.
inc dh ;行.
mov dl,24 ;列.
mov cl,2 ;字符属性.
call show_str
pop cx
loop nfz1
;;;;;;;;;;;;;显示收入;;;;;;;;;;;;;
push di ;收入区的数据的首地址暂存在栈中,为显示人均收入做准备.
mov cx,21 ;21年的收入循环处理.
mov bl,2 ;行号先送入bl中.
srz1:push cx
mov ax,es:[di] ;data段中的数据是连续存放的,es:di已经指向了收入区的首地址.呵呵,这样处理是因为我懒,不想一个一个的去数.
mov dx,es:[di+2] ;dword数据的高位.
call dtoc
mov dh,bl ;行
mov dl,34 ;列(每种数据在屏幕中互相相距10个格子).
mov cl,2 ;字符属性.
call show_str
add di,4 ;指向下一个欲处理的数值的首地址.
inc bl ;指向下一行.
pop cx
loop srz1
;;;;;;;;;;;;;显示雇员数;;;;;;;;;;;;;
push di ;雇员数区的数据的首地址暂存在栈中,为显示人均收入做准备.
mov bl,2 ;行号先送入bl中.
mov cx,21
gyz1:push cx
mov ax,es:[di] ;雇员是word型数
mov dx,0
call dtoc
mov dh,bl ;行
mov dl,44 ;列(每种数据在屏幕中互相相距10个格子).
mov cl,2 ;字符属性.
call show_str
add di,2 ;指向下一个欲处理的数值的首地址.
inc bl ;指向下一行.
pop cx
loop gyz1
;;;;;;;;;;;;;显示人均收入;;;;;;;;;;;;;
pop bp ;雇员数区的数据的首地址.
pop di ;收入区的数据的首地址.
mov bl,2
mov cx,21
rjz1:push cx
mov ax,es:[di] ;收入数据的低位.
mov dx,es:[di+2] ;收入数据的高位.
mov cx,es:[bp] ;雇员数.
call divdw ;计算人均收入,取整.
call dtoc ;处理人均收入数值为字符串.
mov dh,bl
mov dl,54
mov cl,2
call show_str ;显示人均收入.
add di,4
add bp,2
inc bl
pop cx
loop rjz1
mov ax,4c00h
int 21h
;@@@@@@数值转字符串子程序@@@@@@
;名称:dtoc(数值转字符串)
;功能:将dword型或word型数转变为表示十进制数的字符串,字符串以0为结尾符,并在指定的屏幕位置显示数值.
;参数:(ax)=dword型数据的低16位.
; (dx)=dword型数据的高16位.
; ds:si指向字符串首地址.
;返回:无
;这个子程序不能称为数值显示,它实现的功能只是对数值进行字符串的转变,并为字符串显示子程序做了一些必要的处理.当然这两个子程
;序可以融为一体,我做过试验,可以实现,但是操作比较复杂,特别是对栈的操作,虽然比较费脑筋,但是我对于栈的实质有了更加深入的了解.
dtoc:
push cx
push ax
push dx
push si
mov cx,0
push cx ;作为字符串处理结束标记.
dc1:mov cx,10 ;除数(为取得数据的十进制各数位做准备).
call divdw ;取余数(循环取得各数位,从低位到高位).
add cx,30H ;数位转换为ASCII码.
push cx ;暂存数位的ASCII码.
mov cx,dx ;判断商是否为零来确定数据字符串处理结束,必须满足dx和ax同时为零,以下作此处理.
jcxz dc2
jmp short dc1
dc2:mov cx,ax
jcxz clzc
jmp short dc1
clzc:pop cx ;栈顶是数据的最高位的ASCII码,当数值以字符串的形式在内存中存放时是顺序存放的,就是说数值的最高位的ASCII码
;存放在存储这个数值字符串的内存段中的最低位,依此类推.
mov [si],cl ;在指定的zczfc的段中依次保存字符串,因为余数小于0ah,加上30H后不会大于FFH,所以数位的ASCII码一定是只在cl中.
mov ch,0 ;ch和cl结合来判定cx是否为零.
jcxz okdc ;当遇到前面最先入栈的0时字符串处理结束.
inc si ;指向下一个保存位置
jmp short clzc ;处理下一个数位ASCII码.
okdc:pop si ;作为字符串结束的标记0已经写在字符串的尾部.
pop dx
pop ax
pop cx
ret
;@@@@@@不会溢出的除法子程序@@@@@@
;子程序描述
;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型.
;参数:(ax)=dword型数据的低16位
; (dx)=dword型数据的高16位
; (cx)=除数
;返回:(dx)=结果的高16位,(ax)=结果的低16位,(cx)=结果的余数.
;公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
; X:被除数.范围:[0,FFFFFFFF]
; N:除数.范围:[0,FFFF]
; H:X高16位,范围:[0,FFFF]
; L:X低16位,范围:[0,FFFF]
; int():描述性运算符,取商
; rem():描述性运算符,取余数
;这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算.
;因为公式中[rem(H/N)*65536+L]/N小于65536,所以int{rem(H/N)*65536+L]/N}就是32位返回值的低16位,而rem{rem(H/N)*65536+L]/N}就是返
;回值的余数.因int(H/N)小于65536,所以int(H/N)*65536中的int(H/N)就是返回值的高16位.也就是说,假设返回值为m(0≤m≤FFFFFFFF)的自
;然数,任意一个0≤m≤FFFFFFFF的数m都可以分解为a*65536+b+c(余数),如果设定dx中存放m的高16位,那么a就放在dx中,b放在ax中,(dx)的
;实际意义是m中的a*65536.
divdw:
push bx
push ax ;暂存X的低16位L.
push dx ;暂存X的高16位H.
;----------处理int(H/N)*65536---------------------
mov dx,0 ;置H为dword型,(dx)是H的高16位.
pop ax ;dx出栈送入ax中作为H的低16位.
div cx ;H/N.
mov bx,ax ;(ax)是int(H/N)作为返回值的高16位暂存在bx中.
;------处理[rem(H/N)*65536+L]/N.设rem(H/N)*65536+L=a,rem(H/N)作为dword型数据a的高16位放在dx中,L作为低16位放在ax中.------
pop ax ;(dx)=rem(H/N),(ax)=L.
div cx ;[rem(H/N)*65536+L]/N.
mov cx,dx ;(dx)作为返回值的余数送入cx中.
mov dx,bx ;返回值的高16位.(ax中存放的是返回值的低16位)
pop bx
ret
;@@@@@@显示字符串子程序@@@@@@
;子程序描述:
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串.
;参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
; (cl)=颜色,ds:si指向字符串的首地址.
;返回:无
show_str:
push es
push ax
push dx
push di
push cx
push bx
push si
mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov di,ax ;确定行.
add dl,dl ;列乘2是字符.
mov dh,0
mov bx,dx
mov al,cl
s:mov ah,[si] ;以下写入显存.
mov es:[di][bx],ah
mov es:[di][bx+1],al
mov ch,0
mov cl,[si]
jcxz ok
add bx,2
inc si
jmp s
ok:
pop si
pop bx
pop cx
pop di
pop dx
pop ax
pop es
ret
code ends
end start
终于实现了,当看到屏幕上正确的显示了字符时,别提多高兴了!其间的艰辛现在回忆起来那真是一种享受. \(^o^)/
[ 游客 发表于 2009-03-24 14:14 ]
很不错,而且过程分析和描述的也很清楚。
顶了。呵呵
[ jiajiade01 发表于 2009-03-24 22:07 ]
谢谢老师的鼓励.您们才是让我们这些学习者值得尊敬的,在百忙中抽出时间认真的批阅这些枯燥的数据是很不容易的,致敬!!!
[ 游客 发表于 2009-04-20 18:13 ]
很好很强大 值得学习
顶了
[ rswjf 发表于 2009-06-03 22:57 ]
刚刚做完这个课程,真花时间。一看就知道你做得过程很清楚。我的。。。。。明天再改了。
[ rswjf 发表于 2009-06-03 23:10 ]
你的子程序描述的太好了,向你学习!!!
[ 游客 发表于 2009-12-23 11:15 ]
谢谢你啊 呵呵
[ springaccount 发表于 2010-01-06 17:10 ]
O(∩_∩)O哈哈~ 谢谢老师的精彩点评 我又有激情了……