《王爽汇编》课程设计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 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章写的,我们讨论讨论?还有就是我怎么和你一样的把程序发上来啊,我只会这样的复制发上来。不方便观看哦。
工具栏里有个“代码”按钮,图标是“<>”,把代码复制进去就行了。
你说如何编译连接多个文件,其实不是不就是用INT中断来完成的,这个和那个原理上来说应该是一样的。 没看完~~~~~~ 先顶,好同志
页:
[1]