汇编网首页登录博客注册
jiajiade01的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
最新留言
友情链接

[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^)/
评论次数(7)  |  浏览次数(1646)  |  类型(汇编作业) |  收藏此文  | 

[  游客   发表于  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哈哈~  谢谢老师的精彩点评 我又有激情了……

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码