wangkaichao2 发表于 2011-9-24 19:34:12

《王爽汇编》课程设计1,如何编译连接多文件

本帖最后由 wangkaichao2 于 2011-9-24 20:35 编辑



;;------------------------------------------------------------------------------------------
;;                              王爽汇编课程设计1(第211页)
;;                感谢王爽老师的这本《汇编语言》,让我受益匪浅,尤其是代码段,数据段,栈段的讲解,
;;      太经典了,看了之后已经对这本书爱不释手,以前看过单片机的的汇编,8086的汇编刚学了两
;;      周,下面是我对课程设计1的实现。
;;      求助:
;;      1)我觉得我写的代码很不规范,希望高手在编写规范的和高质量的汇编代码方面给我多提出意见。
;;      2)我不知道如何将汇编的多个子模块分别放在多个*.asm文件里,然后像C语言一样编译连接相互
;;                调用的各个模块文件,希望高手能给出帮助,谢谢
;;                                                                                                                作者:wangkaichao2
;;-----------------------------------------------------------------------------------------
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'
      ;0 + 21 * 4 = 84
      dd 16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065, 97479, 140417, 197514
      dd 345980, 590827, 803530, 1183000, 1843000, 2759000, 3753000, 4649000, 5937000
      ;84 + 21 * 4 = 168
      dw 3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1442, 2258, 2793, 4037, 5735, 8226
      dw 11542, 14430, 15257, 17800
      ;168 + 2 * 21 = 210
data ends

table segment
      db 80 dup (0)
table ends

code segment
;;------------------------------------------------------------------------------------
;;                              王爽汇编课程设计1(第211页)
;;                                                MAIN
;;------------------------------------------------------------------------------------
start:
      mov ax, data
      mov es, ax
      mov di, 0
      mov bx, 0

      mov ax, table
      mov ds, ax      ;指定table的段地址
      mov si, 0
      
      mov dh, 3      ;行号
      mov dl, 0      ;列号
      mov cx, 21
next_line:
      push cx                ;循环体中用到cl
      call fill_table
      inc dh                ;行号加1,显示下一行
      mov cl, 2      ;绿色字
      call show_str      ;入参(dh),(dl),(cl),ds:si
      add di, 4      ;如果把data段看作结构体数组,(di)就是结构体数组中“年份”和“总收入”下标
      add bx, 2      ;(bx)是结构体数组中“公司雇员”的下标
      pop cx
      loop next_line
      
      mov ax, 4c00H
      int 21H
;;------------------------------------------------------------------------
                ;;名称:show_str
                ;;功能:在指定位置,用指定的颜色,显示一个用0结束的字符串
                ;;入参:(dh)=行号(0~24),(dl)=列号(0~79),(cl)=颜色,
                ;;                ds:si指向字符串的首地址
                ;;返回值:无
;;------------------------------------------------------------------------
show_str:      ;dh * A0H + dl定位显存偏移起始地址
      push dx
      push ds
      push si

      push ax
      push bx      ;显存偏移地址
      push es      ;显存段地址
      push cx      ;保存ch中的未知数据

      mov ax, 0b800H
      mov es, ax
      mov ax, 0a0H      ;一行a0H个字节
      mul dh                        ;计算第(dh)行的起始偏移地址
      mov dh, 0                ;清零,只保留dl中的列数
      add ax, dx                ;加上列地址,不行的话改为mov dh, 0;add ax, dx
      add ax, dx                ;一列占两个字节
      mov bx, ax                ;es:bx = 字符串在显存中的起始地址
      mov ah, cl                ;ah存储颜色

      mov ch, 0

showChar:      ;将字符串循环填充到显存
      mov cl,
      jcxz toNull
      mov al, cl                ;al存储ASCII码
      mov es:, ax      ;将ASCII码和属性送入显存
      add bx, 2
      inc si
      jmp short showChar
      
toNull:
      pop cx
      pop es
      pop bx
      pop ax

      pop si
      pop ds
      pop dx
      ret                ;;show_str end

;;-------------------------------------------------------------------------------
;;      名称:dtoc
;;      功能:将dword型数据转变为表示十进制数的字符串,字符串以0为结尾符。
;;      参数:(dx)=dword型高16位
;;                  (ax)=dword型低16位
;;                  ds:si指向字符串首地址
;;      返回:无
;;      应用举例:将12666以十进制形式在屏幕的8行3列,用绿色显示
;;      该子模块调用了show_str, divdw子程序段
;;-------------------------------------------------------------------------------
dtoc:
      push dx
      push ax
      push ds
      push cx

      push si

next_div:
      mov cx, 10
      call divdw      ;商(dx)(ax),余数(cx)
      add cx, 30H
      mov , cl      ;ASCII占一个字节
      inc si
      ;;判断(ax)是否等于0
      mov cx, ax
      add cx, 1                ;cx先增1,改成 inc cx ?
      loop next_div      ;cx先减1,if(cx != 0),商不为0
      ;;在(ax)等于0的前提下,判断(dx)是否等于0
      mov cx, dx
      jcxz div_end      ;if(dx==0),商为0,循环结束
      loop next_div

div_end:
      mov byte ptr , 0      ;字符串0结束符
      pop si                                        ;ds:si=字符串起始地址
      call str_reserve                ;颠倒字符串

      pop cx
      pop ds
      pop ax
      pop dx
      ret                              ;;dtoc子程序结束

;;--------------------------------------------------------------------------------------
;;      名称:divdw
;;      功能:进行不会产生溢出的除法运算,被除数dword型,除数word型
;;      入参:(dx) = 被除数高16位,(ax)= 被除数第16位,(cx) = 除数
;;      返回值:(dx) = 商高16位,(ax) = 商第16位,(cx) = 余数
;;                        余数为word型,结果为dword型
;;      int():描述性运算符,取商,比如,int(38/10)=3
;;      rem():描述性运算符,取余,比如,rem(38/10)=8
;;      X:被除数,N:除数
;;      公式:X/N = int(H/N)*10000H + /N
;;--------------------------------------------------------------------------------------
divdw:
      push bx
      push ax                ;被除数低16位入栈
      mov ax, dx      ;处理高16被除数
      mov dx, 0
      div      cx                ;int(H/N)*10000H
      mov bx, ax      ;bx暂存高16位除后的商
      pop ax                ;获取被除数低16位
      div cx                ;/N
      mov cx, dx      ;cx存放余数
      mov dx, bx      

      pop bx
      ret                        ;;end divdw

;;-------------------------------------------------------------------------------------
;;      名称:str_reserve
;;      功能:将数据段中的以0结尾字符串逆序排列
;;      参数:ds:si = 字符串首地址
;;      返回值:(cx) = 字符串长度
;;      该子模块调用了get_str_length
;;-------------------------------------------------------------------------------------
str_reserve:      
      push ds
      push si                ;字符串前后交换时,(si)从第一个字符开始向中间移动
      push di                ;字符串前后交换时,(di)从最后一个字符开始向中间移动
      push ax                ;除法操作
      call get_str_length      ;字符串长度在(cx)中
      push cx                ;将字符长度入栈

      mov di, si
      add di, cx
      dec di                ;(di)=最后一个字符的下标

      mov ax, cx
      mov cl, 2
      div cl                ;该8位除法只针对长度小于512的字符串,>=512产生除法溢出
      mov ah, 0
      add ax, si      ;;(ax)=字符中间下标,

next_char_reserve:
      mov cx, ax      ;(ax)是字符串中间下标
      sub cx, si      ;if(cx == 0),字符串逆序完成
      jcxz str_reserve_end
      ;;交换前后字符
      mov cl,
      mov ch,
      mov , cl
      mov , ch
      dec di
      inc si
      jmp next_char_reserve

str_reserve_end:
      pop cx                ;字符创长度出栈

      pop ax
      pop di
      pop si
      pop ds
      ret                        ;end str_reserve
;;-------------------------------------------------------------------------------------
;;      名称:get_str_length
;;      功能:获取一个以0结束的字符串的长度
;;      入参:ds:si = 字符串首地址
;;      返回值:(cx) = 字符串长度
;;-------------------------------------------------------------------------------------
get_str_length:      ;返回值(cx)
      push ds
      push si
      mov cx, 0
next_char:
      mov cl,
      jcxz str_end
      inc si
      jmp short next_char
str_end:
      mov cx, si
      pop si
      pop ds
      sub cx, si      ;(cx)-(si)的值就是字符串长度
      ret                        ;end get_str_length

;;----------------------------------------------------------------------------------
;;      名称:fill_table
;;      功能:将data段(结构体数组)中的所有字段,转换成以零结束的字符串,存储到table段中,
;;      格式如下:
;;                        0123456789012345678901234567890123456789|
;;                        1975      16      3         5         |结束符
;;      入参:ds:si = table段首地址,es:di = ,es:bx
;;      补充说明:      es:.0 = data中某一年份(4个byte)首地址
;;                              es:.84 = data中某一年份总收入(dword)首地址
;;                              es:.168 = data中某一年份公司雇员(dw)字段首地址
;;      返回值:无
;;      该子模块调用了fill_blank,get_str_length,dtoc
;;----------------------------------------------------------------------------------
fill_table:
      push es
      push di                ;;(es):(di) = data中的数据
      push bx                ;;(es):(bx) = data中的数据

      push ds
      push si                ;;(ds):(si) = table中的数据

      push cx                ;;控制各种循环及jcxp指令等

      push ax      
      push dx                ;;除法操作入参

;;填充年份
      mov cx, 4
      push di
s0:
      mov al, es:
      mov , al
      inc di
      inc si
      loop s0
      pop di

;;填充空格
      mov cx, 10      ;;cx绝对下标
      call fill_blank      ;入参(cx), ds:si
      mov si, cx                ;ds:si = 空格后(收入)字符串起始地址
;;填充公司收入      (si)=10H
      mov ax, es:.84
      mov dx, es:.84
      call dtoc
      call get_str_length      ;(cx)=收入字符串长度
      add si, cx                        ;ds:si=收入字符串0结束符地址
;;填充空格
      mov cx, 20
      call fill_blank
      mov si, cx
s4:      ;;填充雇员数 2字节
      mov ax, es:168      ;入参
      mov dx, 0                        ;入参
      call dtoc
      call get_str_length
      add si, cx
;;填充空格,上面入栈的(di)没有出栈是因为计算人均收入时还要用到现在减半的(di)
      mov cx, 30
      call fill_blank
      mov si, cx
;;填充人均收入
      mov cx, es:168      ;入参,这里的(di)是减半后的(di)
      mov ax, es:84      ;入参
      mov dx, es:84      ;入参
      call divdw

      call dtoc
      call get_str_length
      add si, cx
;;填充空格
      mov cx, 40
      call fill_blank
      mov si, cx
;;填充字符串结束符
      mov byte ptr , 0
;;end
      pop dx
      pop ax

      pop cx

      pop si
      pop ds

      pop bx
      pop di
      pop es
      ret                ;;end fill table
;;--------------------------------------------------------------------------------------
;;名称:fill_blank
;;功能:将指定的一段偏移内存填充为空格字符
;;入参:ds:si = 填充空格起始下标,(cx) = 填充空格截止下标,该字节不会被空格填充
;;返回值:无
;;--------------------------------------------------------------------------------------
fill_blank:                ;;入参(cx)=填充空格截止下标,ds:si = 填充空格起始下标
      push ds
      push si
      push bx

fill_next_blank:
      push cx                ;填充空格截止下标入栈
      sub cx, si
      jcxz fill_blank_end      ;(cx)==0时,栈顶为(cx)
      mov bl, 20H
      mov , bl
      inc si
      pop cx                ;填充空格截止下标出栈
      jmp fill_next_blank

fill_blank_end:
      pop cx

      pop bx
      pop si
      pop ds
      ret                ;;end fill_blank
;;-------------------------------------------------------------------------------------
code ends
end start

flyue 发表于 2011-9-26 01:02:56

本帖最后由 flyue 于 2011-9-26 20:41 编辑





assume cs:code,ss:ss1,ds:data
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1982'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97497,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
ss1 segment
db 64 dup(0)
ss1 ends
code segment
start:mov ax,data
      mov es,ax
   mov ax,ss1
   mov ss,ax
   mov dh,3
   mov dl,3
      mov bx,0
   mov di,0
   mov cx,21
pr:call show_str
   push dx
      mov ax,es:            ;将DWORD中的第一个数据低位送入AX
   mov dx,es:            ;将DWORD中的第一个数据高位送入DX
   add si,40                      ;将数据位置后移20位
   call dtoc      
   mov ax,es:             ;将WORD中的第一个数据送入AX中      
   mov dx,0
   add si,40                      ;将数据位置后移20位
   call dtoc
   push cx
mov ax,es:               
mov dx,es:
mov cx,es:
call divdw
add si,40                        ;将数据位置后移20位
call dtoc
pop cx
pop dx
add dh,1                         ;进入数据的下一行位置
add di,2
   loop pr
mov ax,4c00h
int 21h
   
dtoc:push si                         ;SI入栈,保存SI值
   push cx                         ;CX入栈,保存CX值
push dx                         ;DX入栈,保存DX值
push bx                         ;BX入栈,保存BX值
mov bx,0                        ;计算当前DWORD型数据的位数,设置其为0
   s:mov cx,10                     ;除数为10,S段开始
       call divdw                  ;调用除法溢出处理子程序
      push cx                        ;保存余数CX
   inc bx                         ;DWORD型数据位数自加1
   mov cx,ax
   jcxz s1                        ;当最后一位余数是0时跳转至S1
   jmp short s                  ;跳转至S
s1:pop cx                        ;取出CX值,S1段开始
    dec bx                        ;DWORD型数据位数自减1
    add cl,30h                  ;将CX值转换为ASCII码
    mov ds:,cl                ;将CX值送入数据段当前地址SI中
    mov dl,1                      ;将颜色值送入DX
    mov ds:,dl            ;;将颜色送入当前地址SI后一位地址中
    add si,2                        ;数据地址自加2
    mov cx,bx                     ;将当前BX值送入CX中,DWORD型数据位数
    jcxz s2                     ;当DWORD型数据位数为0时,跳转至S2
    jmp short s1                  ;跳转至S1
s2:pop bx                        ;取出BX值
    pop dx                        ;取出DX值
    pop cx                        ;取出CX值
    pop si                        ;取出SI值
    ret
divdw:push bx                        ;BX入栈,保存BX值
       push ax                     ;DWORD型数据低16位AX入栈
       mov ax,dx                     ;将DWORD型数据高16为AX中
       mov dx,0                      ;将DX值写入0
    div cx                        ;DWORD数据除以CX
    mov bx,ax                     ;结果商送入BX中
    pop ax                        ;取出DWORD型数据低16位AX中
    div cx                        ;以余数DX为高16位CX,除以CX
       mov cx,dx                     ;将余数送入CX
    mov dx,bx                     ;将结果商高16位送入DX中
    pop bx                        ;BX出栈,还原BX值
    ret                           ;返回
show_str:push dx
         push cx
   push ax                     ;AX入栈,保存AX值
   mov si,0                  ;将数据指向数据段0位置
      mov ah,dh                  ;将数据行数送入AH
         mov al,160                  ;将一行总字节数送入AL
         mul ah                      ;进行AH八位乘法,得到数据送入行数的首位地址
   mov dh,0                  ;将DX高位清零,得到数据列偏移大小
         add ax,dx                  
   add ax,dx                  
   add si,ax                   ;数据首位地址加列偏移大小得到数据写入位置
   mov ax,0b800h               
   mov ds,ax                   ;送入数据段段地址
       mov cx,4         
      l: mov dl,01
   mov al,es:               ;将文本数据的首个字符送入AL中
   mov ds:,al            ;将AL中数据送入当前数据写入地址
   mov byte ptr ds:,dl   ;将文字颜色写入数据写入地址后一位地址中
   inc bx                      ;文本数据地址自加一
   add si,2                  ;数据写入地址自加2
   loop l                      ;跳转至l
ok: pop ax                     ;AX出栈,取回AX值
      pop cx
pop dx
   
      ret                         ;返回
code ends
end start


我刚看完10章写的,我们讨论讨论?还有就是我怎么和你一样的把程序发上来啊,我只会这样的复制发上来。不方便观看哦。


wangkaichao2 发表于 2011-9-26 16:33:23

工具栏里有个“代码”按钮,图标是“<>”,把代码复制进去就行了。

flyue 发表于 2011-9-29 02:11:44

你说如何编译连接多个文件,其实不是不就是用INT中断来完成的,这个和那个原理上来说应该是一样的。

阿斯登天 发表于 2011-10-1 00:42:51

没看完~~~~~~ 先顶,好同志
页: [1]
查看完整版本: 《王爽汇编》课程设计1,如何编译连接多文件